Validare HMAC
Validarea HMAC este modul prin care FastOG verifică că fiecare cerere de imagine OG bazată pe abonament provine de pe propriul tău server — URL-urile semnate îți protejează creditele. Înregistrează-te, apoi alege un plan Dynamic API și creează-ți cheia API și secretul HMAC din panoul de control; randările API plătite sunt curate. Această pagină acoperă regulile din partea serverului pentru imaginile Open Graph dinamice, de la semnarea canonică a interogărilor până la compararea în timp constant.
Prezentare generală
Validarea semnăturii HMAC-SHA256 a fost adăugată la endpoint-ul /api/v1/og pentru a preveni utilizarea neautorizată a cotei API. Aceasta asigură că doar cererile provenite de pe site-ul legitim pot genera imagini OG.
Arhitectură
Autentificare pe două niveluri:
- Semnătură HMAC (primul) - Validează că solicitarea provine dintr-o sursă de încredere (site-ul web)
- Cheie API (al doilea) - Identifică contul de utilizator pentru contabilizarea cotei de randare
Un singur middleware (ApiKeyAuth): verifică semnătura (parametrii de interogare) și rezolvă cheia API pentru contabilizarea cotei de randare într-o singură trecere — eșuează rapid la semnături incorecte.
Parametri de interogare, nu anteturi: Deoarece imaginile OG sunt preluate de crawler-ele rețelelor sociale (LinkedIn, Twitter, Facebook) care nu trimit anteturi personalizate, semnătura este transmisă în parametrii URL de interogare:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementare
Fișiere create/modificate
app/Http/Middleware/HmacSignature.php- Middleware nou care validează semnăturile HMAC (parametri de interogare)routes/api.php- Adăugat middleware HMAC pe ruta OGbootstrap/app.php- Înregistrat aliasul middleware-uluihmacapp/Models/ApiKey.php- Adăugat câmpulhmac_secret(secrete per utilizator)app/Http/Controllers/DashboardController.php- Generează secret HMAC la crearea cheii APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migrare pentru coloana hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte- Afișează secretul HMAC utilizatorului (o singură dată)docs/hmac-signature-guide.md- Ghid de implementare pentru clienttests/Feature/HmacSignatureTest.php- 8 teste care trec
Algoritmul de semnătură
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 face parte din interogarea canonică, deci semnătura este legată de cheia API. Modificarea oricărui parametru (title, template, image, …) sau a cheii în sine schimbă interogarea canonică → nepotrivire de semnătură → 401 → fără randare, fără consum de cotă. Parametri identici → semnătură identică → URL identic → memorabil în cache la edge pentru totdeauna, redat fără a fi numărat.
Funcții de securitate
- Semnătură cu adresare pe conținut - Orice modificare de parametru → 401 → fără randare, fără consum de cotă (URL-ul în sine este dovada)
- Fără timestamp - URL-urile sunt încorporate în HTML public + memorate în cache pentru totdeauna; trebuie să reziste mai mult de 5 minute
- Comparare în timp constant (
hash_equals()) - Previne atacurile de tip timing - Cheie API în mesaj - Semnătura este legată de cheie; schimbarea cheilor eșuează
- Specifică căii - Semnătura este validă doar pentru calea exactă
- Specifică metodei - Semnătura GET nu va funcționa pentru POST
- Secrete per utilizator - Fiecare cheie API are propriul secret HMAC, poate fi revocat independent (rotație = întrerupător de urgență pentru URL-uri scurse)
Configurare
Generarea secretului (automat)
Secretul HMAC este generat automat atunci când creezi o cheie API în panoul de control (dashboard). Este afișat o singură dată, împreună cu cheia API, apoi nu mai este afișat niciodată.
Generare manuală (dacă este necesar):
openssl rand -hex 32
Schema bazei de date
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Variabile de mediu
Nu este necesară nicio variabilă de mediu globală - secretele sunt stocate per utilizator în tabelul api_keys.
Exemplu de utilizare (backend-ul site-ului 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}" />
Răspunsuri de eroare
| Status | Cod | Descriere |
|---|---|---|
| 401 | missing_signature |
Parametrul de interogare key sau s lipsește |
| 401 | invalid_signature |
Semnătura nu corespunde interogării canonice (modificată sau secret greșit) |
Testare
Rulează testele:
php artisan test --filter HmacSignatureTest
Toate cele 8 teste trec:
- ✅ Solicitarea fără semnătură este respinsă
- ✅ Solicitarea cu parametrul cheie lipsă este respinsă
- ✅ Parametrul de conținut modificat (titlul schimbat) este respins
- ✅ Parametrul adăugat este respins
- ✅ Semnătura invalidă este respinsă
- ✅ Semnătura validă trece verificarea HMAC
- ✅ Semnătura este specifică căii
- ✅ Semnătura este specifică metodei
- ✅ Cheile API diferite au secrete diferite
Impact asupra performanței
- Nesemnificativ - calculul HMAC-SHA256 durează ~0,1 ms per cerere
- Fără apeluri de rețea suplimentare - validarea este locală
- Eșuează rapid - semnăturile invalide sunt respinse înainte de căutarea cheii API
Considerații de securitate
- Nu expune niciodată secretul în codul client-side - Semnează doar pe server
- Folosește HTTPS - HMAC protejează împotriva cererilor neautorizate, HTTPS protejează împotriva atacurilor MITM
- Rotește secretele periodic - Mai ales dacă sunt compromise (necesită regenerarea cheii API)
- Monitorizează semnăturile eșuate - Eșecurile repetate pot indica atacuri
- Izolare per utilizator - Fiecare cheie API are propriul secret HMAC, compromiterea uneia nu o afectează pe celelalte
Referințe
- Ghid de semnături HMAC pentru clienți
- Ghid de pornire rapidă
- OWASP: Fișă de referință pentru stocarea criptografică
- PHP: hash_hmac()