Validazione HMAC
La validazione HMAC è il metodo con cui FastOG verifica che ogni richiesta di immagine OG basata su abbonamento provenga dal tuo server — gli URL firmati proteggono i tuoi crediti. Registrati, poi scegli un piano API dinamico e crea la tua chiave API e il segreto HMAC nel pannello di controllo (dashboard); i rendering API a pagamento sono puliti. Questa pagina copre le regole lato server dietro le immagini Open Graph dinamiche, dalla firma canonica della query al confronto a tempo costante.
Panoramica
La validazione della firma HMAC-SHA256 è stata aggiunta all'endpoint /api/v1/og per prevenire l'uso non autorizzato della quota API. Questo garantisce che solo le richieste provenienti dal sito web legittimo possano generare immagini OG.
Architettura
Autenticazione a due livelli:
- Firma HMAC (primo) - Verifica che la richiesta provenga da una fonte attendibile (il sito web)
- Chiave API (secondo) - Identifica l'account utente per la contabilizzazione della quota di rendering
Middleware singolo (ApiKeyAuth): verifica la firma (parametri di query) e risolve la chiave API per la contabilizzazione della quota di rendering in un unico passaggio — fallisce rapidamente in caso di firme non valide.
Parametri di query, non header: Poiché le immagini OG vengono recuperate dai crawler dei social network (LinkedIn, Twitter, Facebook) che non inviano header personalizzati, la firma viene passata nei parametri di query dell'URL:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementazione
File creati/modificati
app/Http/Middleware/HmacSignature.php- Nuovo middleware per la validazione delle firme HMAC (parametri di query)routes/api.php- Aggiunto middleware HMAC alla rotta OGbootstrap/app.php- Registrato l'alias del middlewarehmacapp/Models/ApiKey.php- Aggiunto campohmac_secret(segreti per utente)app/Http/Controllers/DashboardController.php- Genera il segreto HMAC alla creazione della chiave APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migrazione per la colonna hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte- Mostra il segreto HMAC all'utente (una sola volta)docs/hmac-signature-guide.md- Guida all'implementazione lato clienttests/Feature/HmacSignatureTest.php- 8 test superati
Algoritmo di 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 fa parte della query canonica, quindi la firma è vincolata alla chiave API. Modificare qualsiasi parametro (titolo, template, immagine, …) o la chiave stessa cambia la query canonica → firma non corrispondente → 401 → nessun rendering, nessun conteggio quota. Parametri identici → firma identica → URL identico → memorizzabile nella cache per sempre, riproducibile senza conteggio.
Funzionalità di sicurezza
- Firma basata sul contenuto - Qualsiasi modifica ai parametri → 401 → nessun rendering, nessun conteggio quota (l'URL stesso è la prova)
- Nessun timestamp - Gli URL sono incorporati in HTML pubblico e memorizzati nella cache per sempre; devono sopravvivere oltre i 5 minuti
- Confronto a tempo costante (
hash_equals()) - Previene gli attacchi di timing - Chiave API nel messaggio - La firma è vincolata alla chiave; scambiare le chiavi fa fallire la verifica
- Specifico per percorso - La firma è valida solo per il percorso esatto
- Specifico per metodo - Una firma GET non funziona per POST
- Segreti per utente - Ogni chiave API ha il proprio segreto HMAC, revocabile in modo indipendente (rotazione = interruttore di emergenza per URL compromessi)
Configurazione
Genera il segreto (automatico)
Il segreto HMAC viene generato automaticamente quando crei una chiave API nella dashboard. Viene mostrato una sola volta insieme alla chiave API, poi non viene più visualizzato.
Generazione manuale (se necessario):
openssl rand -hex 32
Schema del database
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Variabili d'ambiente
Non è necessaria alcuna variabile d'ambiente globale: i segreti sono memorizzati per utente nella tabella api_keys.
Esempio di Utilizzo (Backend del Sito Web)
// 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}" />
Risposte di errore
| Stato | Codice | Descrizione |
|---|---|---|
| 401 | missing_signature |
Parametro di query key o s mancante |
| 401 | invalid_signature |
La firma non corrisponde alla query canonica (alterata o segreto errato) |
Test
Esegui i test:
php artisan test --filter HmacSignatureTest
Tutti gli 8 test passano:
- ✅ Una richiesta senza firma viene rifiutata
- ✅ Una richiesta con parametro chiave mancante viene rifiutata
- ✅ Un parametro contenuto manomesso (titolo modificato) viene rifiutato
- ✅ Un parametro aggiunto viene rifiutato
- ✅ Una firma non valida viene rifiutata
- ✅ Una firma valida supera il controllo HMAC
- ✅ La firma è specifica per il percorso
- ✅ La firma è specifica per il metodo
- ✅ Chiavi API diverse hanno segreti diversi
Impatto sulle prestazioni
- Trascurabile - Il calcolo HMAC-SHA256 richiede circa 0,1 ms per richiesta
- Nessuna chiamata di rete aggiuntiva - La validazione è locale
- Fail fast - Le firme non valide vengono rifiutate prima della ricerca della chiave API
Considerazioni sulla sicurezza
- Non esporre mai il segreto nel codice lato client - firma solo sul server
- Usa HTTPS - HMAC protegge da richieste non autorizzate, HTTPS protegge da attacchi MITM
- Ruota i segreti periodicamente - soprattutto se compromessi (richiede la rigenerazione della chiave API)
- Monitora le firme fallite - fallimenti ripetuti possono indicare attacchi
- Isolamento per utente - ogni chiave API ha il proprio segreto HMAC; la compromissione di uno non influisce sugli altri
Riferimenti
- Guida alla firma HMAC per i clienti
- Guida rapida
- OWASP: Cheat Sheet per la memorizzazione crittografica
- PHP: hash_hmac()