Subiect

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:

  1. Semnătură HMAC (primul) - Validează că solicitarea provine dintr-o sursă de încredere (site-ul web)
  2. 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 OG
  • bootstrap/app.php - Înregistrat aliasul middleware-ului hmac
  • app/Models/ApiKey.php - Adăugat câmpul hmac_secret (secrete per utilizator)
  • app/Http/Controllers/DashboardController.php - Generează secret HMAC la crearea cheii API
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php - Migrare pentru coloana hmac_secret
  • resources/js/Pages/Dashboard/ApiKeys.svelte - Afișează secretul HMAC utilizatorului (o singură dată)
  • docs/hmac-signature-guide.md - Ghid de implementare pentru client
  • tests/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):

bash
openssl rand -hex 32

Schema bazei de date

sql
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)

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

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:

bash
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

  1. Nu expune niciodată secretul în codul client-side - Semnează doar pe server
  2. Folosește HTTPS - HMAC protejează împotriva cererilor neautorizate, HTTPS protejează împotriva atacurilor MITM
  3. Rotește secretele periodic - Mai ales dacă sunt compromise (necesită regenerarea cheii API)
  4. Monitorizează semnăturile eșuate - Eșecurile repetate pot indica atacuri
  5. Izolare per utilizator - Fiecare cheie API are propriul secret HMAC, compromiterea uneia nu o afectează pe celelalte

Referințe

Referință completă a API

Încearcă gratuit — nu este necesară înregistrarea

Previzualizează și testează imagini OG dinamice în câteva secunde.

Deschide Testerul gratuit de imagini OG

A BeCaSimulations site