Onderwerp

HMAC-validatie

HMAC-validatie is de manier waarop FastOG controleert dat elk op abonnementen gebaseerd OG-afbeeldingsverzoek afkomstig is van jouw eigen server — ondertekende URL's beschermen je credits. Meld je aan, kies daarna een Dynamic API-plan en maak je API-sleutel en HMAC-geheim aan in het dashboard; betaalde API-renders zijn schoon. Deze pagina behandelt de server-side regels achter dynamische Open Graph-afbeeldingen, van canonieke query-ondertekening tot constante-tijdvergelijking.

Overzicht

Er is HMAC-SHA256-handtekeningvalidatie toegevoegd aan het /api/v1/og-eindpunt om ongeautoriseerd gebruik van het API-quotum te voorkomen. Dit zorgt ervoor dat alleen verzoeken van de legitieme website OG-afbeeldingen kunnen genereren.

Architectuur

Authenticatie in twee lagen:

  1. HMAC-handtekening (eerste) – Valideert dat het verzoek afkomstig is van een vertrouwde bron (de website)
  2. API-sleutel (tweede) – Identificeert het gebruikersaccount voor de registratie van het renderquotum

Eén enkele middleware (ApiKeyAuth): verifieert de handtekening (queryparameters) en lost de API-sleutel op voor de registratie van het renderquotum in één keer — faal snel bij ongeldige handtekeningen.

Queryparameters, geen headers: Omdat OG-afbeeldingen worden opgehaald door crawlers van sociale netwerken (LinkedIn, Twitter, Facebook) die geen aangepaste headers meesturen, wordt de handtekening doorgegeven via URL-queryparameters:

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

Implementatie

Gemaakte/gewijzigde bestanden

  • app/Http/Middleware/HmacSignature.php - Nieuwe middleware die HMAC-handtekeningen valideert (queryparameters)
  • routes/api.php - HMAC-middleware toegevoegd aan de OG-route
  • bootstrap/app.php - hmac-middleware-alias geregistreerd
  • app/Models/ApiKey.php - hmac_secret-veld toegevoegd (geheimen per gebruiker)
  • app/Http/Controllers/DashboardController.php - Genereert HMAC-geheim bij het aanmaken van een API-sleutel
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php - Migratie voor de hmac_secret-kolom
  • resources/js/Pages/Dashboard/ApiKeys.svelte - Toont het HMAC-geheim aan de gebruiker (eenmalig)
  • docs/hmac-signature-guide.md - Handleiding voor clientimplementatie
  • tests/Feature/HmacSignatureTest.php - 8 geslaagde tests

Handtekeningalgoritme

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 maakt deel uit van de canonieke query, dus de handtekening is gekoppeld aan de API-sleutel. Het wijzigen van een parameter (titel, template, afbeelding, …) of de sleutel zelf verandert de canonieke vorm → handtekening komt niet overeen → 401 → geen render, geen quotumtelling. Identieke parameters → identieke handtekening → identieke URL → permanent cachebaar aan de edge, opnieuw af te spelen zonder te tellen.

Beveiligingsfuncties

  • Content-geadresseerde handtekening - Elke parameterwijziging → 401 → geen render, geen quotumtelling (de URL zelf is het bewijs)
  • Geen tijdstempel - URL's worden ingebed in openbare HTML en permanent gecachet; ze moeten langer dan 5 minuten meegaan
  • Constante-tijdvergelijking (hash_equals()) - Voorkomt timingaanvallen
  • API-sleutel in het bericht - De handtekening is gekoppeld aan de sleutel; het wisselen van sleutels mislukt
  • Pad-specifiek - De handtekening is alleen geldig voor het exacte pad
  • Methode-specifiek - Een GET-handtekening werkt niet voor POST
  • Geheimen per gebruiker - Elke API-sleutel heeft zijn eigen HMAC-geheim, onafhankelijk in te trekken (rotatie = kill switch voor gelekt URL's)

Configuratie

Geheime sleutel genereren (automatisch)

De HMAC-geheime sleutel wordt automatisch gegenereerd wanneer je een API-sleutel aanmaakt in het dashboard. Deze wordt één keer weergegeven samen met de API-sleutel en daarna nooit meer getoond.

Handmatige generatie (indien nodig):

bash
openssl rand -hex 32

Databaseschema

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

Omgevingsvariabelen

Er is geen globale omgevingsvariabele nodig — geheime sleutels worden per gebruiker opgeslagen in de api_keys-tabel.

Gebruiksvoorbeeld (website-backend)

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

Foutmeldingen

Status Code Beschrijving
401 missing_signature Ontbrekende key- of s-queryparameter
401 invalid_signature Handtekening komt niet overeen met de canonieke query (gemanipuleerd of verkeerde geheime sleutel)

Testen

Voer de tests uit:

bash
php artisan test --filter HmacSignatureTest

Alle 8 tests slagen:

  • ✅ Verzoek zonder handtekening wordt afgewezen
  • ✅ Verzoek met ontbrekende key-parameter wordt afgewezen
  • ✅ Gewijzigde content-parameter (titel gewijzigd) wordt afgewezen
  • ✅ Toegevoegde parameter wordt afgewezen
  • ✅ Ongeldige handtekening wordt afgewezen
  • ✅ Geldige handtekening doorstaat de HMAC-controle
  • ✅ Handtekening is pad-specifiek
  • ✅ Handtekening is methode-specifiek
  • ✅ Verschillende API-sleutels hebben verschillende geheimen

Prestatie-impact

  • Verwaarloosbaar – HMAC-SHA256-berekening kost ~0,1 ms per verzoek
  • Geen extra netwerkaanroepen – Validatie gebeurt lokaal
  • Snel falen – Ongeldige handtekeningen worden afgewezen vóór de API-sleutelopzoeking

Beveiligingsoverwegingen

  1. Stel het geheim nooit bloot in client-side code – onderteken uitsluitend op de server
  2. Gebruik HTTPS – HMAC beschermt tegen ongeautoriseerde verzoeken, HTTPS beschermt tegen MITM
  3. Roteer geheimen periodiek – vooral als ze gecompromitteerd zijn (vereist het opnieuw genereren van de API-sleutel)
  4. Monitor mislukte handtekeningen – herhaalde mislukkingen kunnen duiden op aanvallen
  5. Isolatie per gebruiker – elke API-sleutel heeft zijn eigen HMAC-geheim; een compromis van de ene heeft geen invloed op de andere

Referenties

Volledige API-referentie

Probeer het gratis — geen aanmelding vereist

Bekijk en test dynamische OG-afbeeldingen in seconden.

Open de gratis OG-afbeeldingstester

A BeCaSimulations site