Tópico

Validação HMAC

A validação HMAC é a forma como a FastOG verifica que cada pedido de imagem OG baseado em subscrição veio do seu próprio servidor — URLs assinados mantêm os seus créditos seguros. Registe-se, escolha um plano Dynamic API e crie a sua chave de API e segredo HMAC no painel de controlo (dashboard); os renders de API pagos são limpos. Esta página cobre as regras do lado do servidor por trás das imagens Open Graph dinâmicas, desde a assinatura canónica de consultas até à comparação em tempo constante.

Visão geral

A validação de assinatura HMAC-SHA256 foi adicionada ao endpoint /api/v1/og para impedir a utilização não autorizada da quota da API. Isto garante que apenas os pedidos provenientes do website legítimo podem gerar imagens OG.

Arquitetura

Autenticação em duas camadas:

  1. Assinatura HMAC (primeira) — Valida que o pedido foi originado de uma fonte confiável (o website)
  2. Chave de API (segunda) — Identifica a conta do utilizador para efeitos de contabilização da quota de renderização

Middleware único (ApiKeyAuth): verifica a assinatura (parâmetros de consulta) e resolve a chave de API para a contabilização da quota de renderização numa única passagem — falha rapidamente em assinaturas inválidas.

Parâmetros de consulta, não cabeçalhos: Como as imagens OG são obtidas pelos crawlers das redes sociais (LinkedIn, Twitter, Facebook) que não enviam cabeçalhos personalizados, a assinatura é passada nos parâmetros de consulta do URL:

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

Implementação

Ficheiros Criados/Modificados

  • app/Http/Middleware/HmacSignature.php - Novo middleware que valida assinaturas HMAC (parâmetros de consulta)
  • routes/api.php - Adicionado middleware HMAC à rota OG
  • bootstrap/app.php - Registado o alias do middleware hmac
  • app/Models/ApiKey.php - Adicionado o campo hmac_secret (segredos por utilizador)
  • app/Http/Controllers/DashboardController.php - Gera segredo HMAC na criação da chave de API
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php - Migração para a coluna hmac_secret
  • resources/js/Pages/Dashboard/ApiKeys.svelte - Mostra o segredo HMAC ao utilizador (uma única vez)
  • docs/hmac-signature-guide.md - Guia de implementação para o cliente
  • tests/Feature/HmacSignatureTest.php - 8 testes a passar

Algoritmo de Assinatura

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 faz parte da consulta canónica, pelo que a assinatura está vinculada à chave de API. Alterar qualquer parâmetro (título, modelo, imagem, …) ou a própria chave altera a consulta canónica → incompatibilidade de assinatura → 401 → sem renderização, sem contagem de quota. Parâmetros idênticos → assinatura idêntica → URL idêntico → armazenável em cache no edge para sempre, reproduzido sem contagem.

Funcionalidades de Segurança

  • Assinatura com endereçamento por conteúdo - Qualquer alteração de parâmetro → 401 → sem renderização, sem contagem de quota (o próprio URL é a prova)
  • Sem timestamp - Os URLs são incorporados em HTML público e armazenados em cache para sempre; têm de sobreviver a 5 minutos
  • Comparação em tempo constante (hash_equals()) - Evita ataques de temporização
  • Chave de API na mensagem - A assinatura está vinculada à chave; trocar de chave falha
  • Específica do caminho - A assinatura só é válida para o caminho exato
  • Específica do método - Uma assinatura GET não funciona para POST
  • Segredos por utilizador - Cada chave de API tem o seu próprio segredo HMAC, podendo ser revogado de forma independente (rotação = interruptor de emergência para URLs divulgados)

Configuração

Gerar Segredo (Automático)

O segredo HMAC é gerado automaticamente ao criar uma chave de API no painel de controlo (dashboard). É apresentado uma única vez juntamente com a chave de API e nunca mais é mostrado novamente.

Geração manual (se necessário):

bash
openssl rand -hex 32

Esquema da Base de Dados

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

Variáveis de Ambiente

Não é necessária nenhuma variável de ambiente global — os segredos são armazenados por utilizador na tabela api_keys.

Exemplo de Utilização (Backend do Website)

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}" />

Respostas de Erro

Estado Código Descrição
401 missing_signature Parâmetro de consulta key ou s em falta
401 invalid_signature A assinatura não corresponde à consulta canónica (alterada ou segredo errado)

Testes

Execute os testes:

bash
php artisan test --filter HmacSignatureTest

Todos os 8 testes passam:

  • ✅ Pedido sem assinatura é rejeitado
  • ✅ Pedido com parâmetro de chave em falta é rejeitado
  • ✅ Parâmetro de conteúdo adulterado (título alterado) é rejeitado
  • ✅ Parâmetro adicionado é rejeitado
  • ✅ Assinatura inválida é rejeitada
  • ✅ Assinatura válida passa na verificação HMAC
  • ✅ A assinatura é específica do caminho
  • ✅ A assinatura é específica do método
  • ✅ Chaves de API diferentes têm segredos diferentes

Impacto no Desempenho

  • Desprezível - O cálculo de HMAC-SHA256 é de ~0,1ms por pedido
  • Sem chamadas de rede adicionais - A validação é local
  • Falha rápida - Assinaturas inválidas são rejeitadas antes da pesquisa da chave de API

Considerações de Segurança

  1. Nunca exponha o segredo em código do lado do cliente - Assine apenas no servidor
  2. Use HTTPS - O HMAC protege contra pedidos não autorizados, o HTTPS protege contra ataques MITM
  3. Rode os segredos periodicamente - Especialmente se forem comprometidos (requer regenerar a chave da API)
  4. Monitore assinaturas falhadas - Falhas repetidas podem indicar ataques
  5. Isolamento por utilizador - Cada chave da API tem o seu próprio segredo HMAC; o comprometimento de uma não afeta as outras

Referências

Referência Completa da API

Experimente gratuitamente — sem necessidade de registo

Pré-visualize e teste imagens OG dinâmicas em segundos.

Abrir o Testador Gratuito de Imagens OG

A BeCaSimulations site