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:
- HMAC-handtekening (eerste) – Valideert dat het verzoek afkomstig is van een vertrouwde bron (de website)
- 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-routebootstrap/app.php-hmac-middleware-alias geregistreerdapp/Models/ApiKey.php-hmac_secret-veld toegevoegd (geheimen per gebruiker)app/Http/Controllers/DashboardController.php- Genereert HMAC-geheim bij het aanmaken van een API-sleuteldatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migratie voor de hmac_secret-kolomresources/js/Pages/Dashboard/ApiKeys.svelte- Toont het HMAC-geheim aan de gebruiker (eenmalig)docs/hmac-signature-guide.md- Handleiding voor clientimplementatietests/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):
openssl rand -hex 32
Databaseschema
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)
// 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:
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
- Stel het geheim nooit bloot in client-side code – onderteken uitsluitend op de server
- Gebruik HTTPS – HMAC beschermt tegen ongeautoriseerde verzoeken, HTTPS beschermt tegen MITM
- Roteer geheimen periodiek – vooral als ze gecompromitteerd zijn (vereist het opnieuw genereren van de API-sleutel)
- Monitor mislukte handtekeningen – herhaalde mislukkingen kunnen duiden op aanvallen
- Isolatie per gebruiker – elke API-sleutel heeft zijn eigen HMAC-geheim; een compromis van de ene heeft geen invloed op de andere
Referenties
- HMAC-handtekeninggids voor clients
- Snelstartgids
- OWASP: Cryptographic Storage Cheat Sheet
- PHP: hash_hmac()