Validação HMAC
A validação HMAC é como a FastOG verifica se cada solicitação de imagem OG baseada em assinatura veio do seu próprio servidor — URLs assinadas mantêm seus créditos seguros. Cadastre-se, escolha um plano de API Dinâmica e crie sua chave de API e segredo HMAC no painel; renderizações de API pagas são limpas. Esta página aborda as regras do lado do servidor por trás das imagens Open Graph dinâmicas, desde a assinatura canônica da consulta até a comparação em tempo constante.
Visão geral
A validação de assinatura HMAC-SHA256 foi adicionada ao endpoint /api/v1/og para impedir o uso não autorizado da cota da API. Isso garante que apenas solicitações do site legítimo possam gerar imagens OG.
Arquitetura
Autenticação em duas camadas:
- Assinatura HMAC (primeira) — Valida se a requisição se originou de uma fonte confiável (o site)
- Chave de API (segunda) — Identifica a conta do usuário para o controle de cota de renderização
Middleware único (ApiKeyAuth): verifica a assinatura (parâmetros de consulta) e resolve a chave de API para o controle de cota de renderização em uma única passada — falhe rapidamente em assinaturas inválidas.
Parâmetros de consulta, não cabeçalhos: Como as imagens OG são buscadas pelos rastreadores de redes sociais (LinkedIn, Twitter, Facebook) que não enviam cabeçalhos personalizados, a assinatura é passada nos parâmetros de consulta da URL:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementação
Arquivos Criados/Modificados
app/Http/Middleware/HmacSignature.php- Novo middleware validando assinaturas HMAC (parâmetros de consulta)routes/api.php- Middleware HMAC adicionado à rota OGbootstrap/app.php- Alias do middlewarehmacregistradoapp/Models/ApiKey.php- Campohmac_secretadicionado (segredos por usuário)app/Http/Controllers/DashboardController.php- Gera segredo HMAC na criação da chave de APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migração para a coluna hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte- Mostra o segredo HMAC ao usuário (uma única vez)docs/hmac-signature-guide.md- Guia de implementação para o clientetests/Feature/HmacSignatureTest.php- 8 testes passando
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, então a assinatura está vinculada à chave de API. Alterar qualquer parâmetro (título, template, imagem, …) ou a própria chave muda o canônico → incompatibilidade de assinatura → 401 → sem renderização, sem contagem de cota. Parâmetros idênticos → assinatura idêntica → URL idêntica → armazenável em cache na borda para sempre, reproduzida sem contagem.
Recursos de Segurança
- Assinatura com endereçamento por conteúdo - Qualquer alteração de parâmetro → 401 → sem renderização, sem contagem de cota (a própria URL é a prova)
- Sem timestamp - URLs são incorporadas em HTML público e armazenadas em cache para sempre; elas precisam sobreviver além de 5 minutos
- Comparação em tempo constante (
hash_equals()) - Previne ataques de timing - Chave de API na mensagem - A assinatura está vinculada à chave; trocar as chaves falha
- Específica ao caminho - A assinatura é válida apenas para o caminho exato
- Específica ao método - A assinatura GET não funciona para POST
- Segredos por usuário - Cada chave de API tem seu próprio segredo HMAC, podendo ser revogado de forma independente (rotacionar = interruptor de emergência para URLs vazadas)
Configuração
Gerar Segredo (Automático)
O segredo HMAC é gerado automaticamente ao criar uma chave de API no painel de controle (dashboard). Ele é exibido uma única vez junto com a chave de API e nunca mais é mostrado novamente.
Geração manual (se necessário):
openssl rand -hex 32
Esquema do Banco de Dados
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
Nenhuma variável de ambiente global é necessária — os segredos são armazenados por usuário na tabela api_keys.
Exemplo de Uso (Backend do Site)
// 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
| Status | Código | Descrição |
|---|---|---|
| 401 | missing_signature |
Parâmetro de consulta key ou s ausente |
| 401 | invalid_signature |
A assinatura não corresponde à consulta canônica (adulterada ou segredo incorreto) |
Testes
Execute os testes:
php artisan test --filter HmacSignatureTest
Todos os 8 testes passam:
- ✅ Requisição sem assinatura é rejeitada
- ✅ Requisição com parâmetro de chave ausente é rejeitada
- ✅ 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
- ✅ Assinatura é específica do caminho
- ✅ 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 leva ~0,1ms por requisição
- Sem chamadas de rede adicionais - A validação é local
- Falha rápida - Assinaturas inválidas são rejeitadas antes da consulta da chave de API
Considerações de Segurança
- Nunca exponha o segredo no código do lado do cliente - Assine apenas no servidor
- Use HTTPS - O HMAC protege contra requisições não autorizadas, o HTTPS protege contra ataques MITM
- Rotacione os segredos periodicamente - Especialmente se comprometidos (exige regenerar a chave de API)
- Monitore assinaturas com falha - Falhas repetidas podem indicar ataques
- Isolamento por usuário - Cada chave de API tem seu próprio segredo HMAC; o comprometimento de uma não afeta as demais
Referências
- Guia de Assinatura HMAC para Clientes
- Guia de Início Rápido
- OWASP: Folha de Dicas de Armazenamento Criptográfico
- PHP: hash_hmac()