Tema

Validación HMAC

La validación HMAC es el mecanismo mediante el cual FastOG verifica que cada solicitud de imagen OG basada en suscripción proviene de tu propio servidor: las URLs firmadas mantienen tus créditos a salvo. Regístrate, elige un plan de API dinámica y crea tu clave de API y secreto HMAC en el panel de control; los renderizados de API de pago están limpios. Esta página cubre las reglas del lado del servidor detrás de las imágenes Open Graph dinámicas, desde la firma canónica de consultas hasta la comparación en tiempo constante.

Resumen

Se ha añadido la validación de firmas HMAC-SHA256 al endpoint /api/v1/og para evitar el uso no autorizado de la cuota de API. Esto garantiza que solo las solicitudes procedentes del sitio web legítimo puedan generar imágenes OG.

Arquitectura

Autenticación en dos capas:

  1. Firma HMAC (primera): valida que la solicitud proviene de una fuente de confianza (el sitio web)
  2. Clave de API (segunda): identifica la cuenta de usuario para el control de cuota de renderizado

Middleware único (ApiKeyAuth): verifica la firma (parámetros de consulta) y resuelve la clave de API para el control de cuota de renderizado en una sola pasada; falla rápido ante firmas incorrectas.

Parámetros de consulta, no cabeceras: dado que los rastreadores de redes sociales (LinkedIn, Twitter, Facebook) obtienen las imágenes OG sin enviar cabeceras personalizadas, la firma se pasa en los parámetros de consulta de la URL:

GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>

Implementación

Archivos creados/modificados

  • app/Http/Middleware/HmacSignature.php - Nuevo middleware que valida firmas HMAC (parámetros de consulta)
  • routes/api.php - Añadido middleware HMAC a la ruta OG
  • bootstrap/app.php - Registrado el alias del middleware hmac
  • app/Models/ApiKey.php - Añadido el campo hmac_secret (secretos por usuario)
  • app/Http/Controllers/DashboardController.php - Genera el secreto HMAC al crear la clave de API
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php - Migración para la columna hmac_secret
  • resources/js/Pages/Dashboard/ApiKeys.svelte - Muestra el secreto HMAC al usuario (una sola vez)
  • docs/hmac-signature-guide.md - Guía de implementación para clientes
  • tests/Feature/HmacSignatureTest.php - 8 pruebas que pasan

Algoritmo de firma

method = HTTP method (e.g., "GET")
path  = request path (e.g., "/api/v1/og")
query = the request's DECODED query params

canonical = every param except `s`, keys sorted (array values sorted too),
            each `key=value` RFC-3986 encoded (unreserved-only), joined with "&"
message   = method + "/" + path + "\n" + canonical
signature = HMAC-SHA256(message, hmac_secret)

Query parameters:
  key=<apiKey>
  s=<signature>
  (no timestamp)

key forma parte de la consulta canónica, por lo que la firma está vinculada a la clave de API. Cambiar cualquier parámetro (título, plantilla, imagen, etc.) o la propia clave cambia la consulta canónica → discrepancia de firma → 401 → sin renderizado, sin contar cuota. Parámetros idénticos → firma idéntica → URL idéntica → cacheable en el edge para siempre, reproducida sin contabilizar.

Características de seguridad

  • Firma con direccionamiento por contenido - Cualquier cambio de parámetro → 401 → sin renderizado, sin contar cuota (la propia URL es la prueba)
  • Sin marca de tiempo - Las URLs se incrustan en HTML público y se cachean para siempre; deben sobrevivir más de 5 minutos
  • Comparación en tiempo constante (hash_equals()) - Evita ataques de temporización
  • Clave de API en el mensaje - La firma está vinculada a la clave; cambiar de clave falla
  • Específica de ruta - La firma solo es válida para la ruta exacta
  • Específica de método - La firma GET no funciona para POST
  • Secretos por usuario - Cada clave de API tiene su propio secreto HMAC, se puede revocar de forma independiente (rotar = interruptor de corte para URLs filtradas)

Configuración

Generar secreto (automático)

El secreto HMAC se genera automáticamente al crear una clave de API en el panel de control. Se muestra una sola vez junto con la clave de API y nunca vuelve a mostrarse.

Generación manual (si es necesario):

bash
openssl rand -hex 32

Esquema de base de datos

sql
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
  COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';

Variables de entorno

No se necesita ninguna variable de entorno global: los secretos se almacenan por usuario en la tabla api_keys.

Ejemplo de uso (backend del sitio web)

javascript
// Node.js example - generate signed OG image URL
const crypto = require("crypto");

// RFC-3986 encode (encodeURIComponent leaves !'()* unencoded).
const enc = (s) =>
    encodeURIComponent(s).replace(
        /[!'()*]/g,
        (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
    );

function generateOgImageUrl(baseUrl, params, apiKey, hmacSecret) {
    const all = { ...params, key: apiKey };

    // Canonical: every param (except s), sorted, RFC-3986 encoded, joined with "&".
    const pairs = [];
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? [...v].sort() : [v])
            pairs.push(`${enc(k)}=${enc(vv)}`);
    }
    pairs.sort();
    const canonical = pairs.join("&");

    const message = `GET/api/v1/og\n${canonical}`;
    const signature = crypto
        .createHmac("sha256", hmacSecret)
        .update(message)
        .digest("hex");

    const url = new URL(baseUrl);
    for (const [k, v] of Object.entries(all)) {
        const isArray = Array.isArray(v);
        for (const vv of isArray ? v : [v])
            url.searchParams.append(isArray ? `${k}[]` : k, vv);
    }
    url.searchParams.set("s", signature);
    return url.toString();
}

// Usage in meta tag
const ogUrl = generateOgImageUrl(
    "https://fastog.com/api/v1/og",
    { title: "Como criar um SaaS", template: "blog" },
    "sk_quick_123abc",
    process.env.HMAC_SECRET,
);

// <meta property="og:image" content="{ogUrl}" />

Respuestas de error

Estado Código Descripción
401 missing_signature Falta el parámetro de consulta key o s
401 invalid_signature La firma no coincide con la consulta canónica (manipulada o secreto incorrecto)

Pruebas

Ejecuta las pruebas:

bash
php artisan test --filter HmacSignatureTest

Las 8 pruebas pasan:

  • ✅ Se rechaza una solicitud sin firma
  • ✅ Se rechaza una solicitud con el parámetro de clave faltante
  • ✅ Se rechaza el parámetro de contenido manipulado (título cambiado)
  • ✅ Se rechaza un parámetro añadido
  • ✅ Se rechaza una firma no válida
  • ✅ Una firma válida supera la comprobación HMAC
  • ✅ La firma es específica de la ruta
  • ✅ La firma es específica del método
  • ✅ Diferentes claves de API tienen secretos distintos

Impacto en el rendimiento

  • Insignificante: el cálculo de HMAC-SHA256 es de ~0,1 ms por solicitud
  • Sin llamadas de red adicionales: la validación es local
  • Fallo rápido: las firmas no válidas se rechazan antes de la búsqueda de la clave de API

Consideraciones de seguridad

  1. Nunca expongas el secreto en código del lado del cliente - Firma solo en el servidor
  2. Usa HTTPS - HMAC protege contra solicitudes no autorizadas, HTTPS protege contra ataques MITM
  3. Rota los secretos periódicamente - Especialmente si se han visto comprometidos (requiere regenerar la clave de API)
  4. Supervisa las firmas fallidas - Fallos repetidos pueden indicar ataques
  5. Aislamiento por usuario - Cada clave de API tiene su propio secreto HMAC, el compromiso de uno no afecta a los demás

Referencias

Referencia completa de la API

Pruébalo gratis: no se requiere registro

Previsualiza y prueba imágenes OG dinámicas en segundos.

Abrir el probador gratuito de imágenes OG

A BeCaSimulations site