HMAC-validering
HMAC-validering er den måde, FastOG verificerer, at hver abonnementsbaseret OG-billedanmodning kommer fra din egen server – signerede URL'er beskytter dine credits. Opret en konto, vælg derefter en Dynamic API-plan, og opret din API-nøgle og HMAC-hemmelighed i dashboardet; betalte API-renderinger er rene. Denne side dækker server-side-reglerne bag dynamiske Open Graph-billeder, fra kanonisk forespørgselssignering til konstant-tids-sammenligning.
Oversigt
Der er tilføjet validering af HMAC-SHA256-signaturer til /api/v1/og-endepunktet for at forhindre uautoriseret brug af API-kvoten. Dette sikrer, at kun forespørgsler fra det legitime website kan generere OG-billeder.
Arkitektur
To-lags godkendelse:
- HMAC-signatur (først) – Validerer, at anmodningen stammer fra en betroet kilde (websitet)
- API-nøgle (derefter) – Identificerer brugerkontoen til afregning af render-kvote
Enkelt middleware (ApiKeyAuth): verificerer signaturen (query-parametre) og løser API-nøglen til afregning af render-kvote i ét gennemløb – fejl hurtigt ved ugyldige signaturer.
Query-parametre, ikke headere: Da OG-billeder hentes af sociale netværkscrawlere (LinkedIn, Twitter, Facebook), som ikke sender brugerdefinerede headere, sendes signaturen i URL-query-parametre:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementering
Oprettede/ændrede filer
app/Http/Middleware/HmacSignature.php- Ny middleware, der validerer HMAC-signaturer (query-parametre)routes/api.php- Tilføjet HMAC-middleware til OG-rutenbootstrap/app.php- Registrerethmac-middleware-aliasapp/Models/ApiKey.php- Tilføjethmac_secret-felt (hemmeligheder pr. bruger)app/Http/Controllers/DashboardController.php- Genererer HMAC-hemmelighed ved oprettelse af API-nøgledatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migration til hmac_secret-kolonneresources/js/Pages/Dashboard/ApiKeys.svelte- Viser HMAC-hemmelighed til brugeren (én gang)docs/hmac-signature-guide.md- Implementeringsguide til klientertests/Feature/HmacSignatureTest.php- 8 bestående tests
Signaturalgoritme
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 er en del af den kanoniske query, så signaturen er bundet til API-nøglen. Ændring af en hvilken som helst parameter (titel, skabelon, billede, …) eller selve nøglen ændrer den kanoniske streng → signaturmismatch → 401 → ingen rendering, ingen kvotetælling. Identiske parametre → identisk signatur → identisk URL → kan cachelagres ved kanten for evigt, afspilles uden at tælle.
Sikkerhedsfunktioner
- Indholdsadresseret signatur - Enhver parameterændring → 401 → ingen rendering, ingen kvotetælling (URL'en er i sig selv beviset)
- Ingen tidsstempel - URL'er indlejres i offentlig HTML og cachelagres for evigt; de skal overleve 5 minutter
- Konstant-tids-sammenligning (
hash_equals()) - Forhindrer timing-angreb - API-nøgle i beskeden - Signaturen er bundet til nøglen; udskiftning af nøgler fejler
- Stispecifik - Signatur er kun gyldig for den præcise sti
- Metodespecifik - GET-signatur virker ikke til POST
- Hemmeligheder pr. bruger - Hver API-nøgle har sin egen HMAC-hemmelighed, som kan tilbagekaldes uafhængigt (rotation = kill switch for lækkede URL'er)
Konfiguration
Generering af hemmelig nøgle (automatisk)
HMAC-hemmeligheden genereres automatisk, når du opretter en API-nøgle i dashboardet. Den vises én gang sammen med API-nøglen og vises derefter aldrig igen.
Manuel generering (hvis nødvendigt):
openssl rand -hex 32
Databaseskema
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Miljøvariabler
Der er ingen global miljøvariabel nødvendig – hemmeligheder gemmes pr. bruger i tabellen api_keys.
Brugseksempel (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}" />
Fejlresponser
| Status | Kode | Beskrivelse |
|---|---|---|
| 401 | missing_signature |
Manglende key- eller s-forespørgselsparameter |
| 401 | invalid_signature |
Signatur matcher ikke den kanoniske forespørgsel (manipuleret eller forkert hemmelighed) |
Testning
Kør testene:
php artisan test --filter HmacSignatureTest
Alle 8 tests består:
- ✅ Anmodning uden signatur afvises
- ✅ Anmodning med manglende nøgleparameter afvises
- ✅ Manipuleret indholdsparameter (titel ændret) afvises
- ✅ Tilføjet parameter afvises
- ✅ Ugyldig signatur afvises
- ✅ Gyldig signatur består HMAC-kontrollen
- ✅ Signatur er sti-specifik
- ✅ Signatur er metode-specifik
- ✅ Forskellige API-nøgler har forskellige hemmeligheder
Ydeevnepåvirkning
- Ubetydelig – HMAC-SHA256-beregning tager ~0,1 ms pr. anmodning
- Ingen ekstra netværkskald – Validering er lokal
- Fejler hurtigt – Ugyldige signaturer afvises, før API-nøgleopslag finder sted
Sikkerhedsovervejelser
- Udsæt aldrig hemmeligheden i klientsidekode – signér kun på serveren
- Brug HTTPS – HMAC beskytter mod uautoriserede anmodninger, HTTPS beskytter mod MITM
- Roter hemmeligheder jævnligt – især hvis de er kompromitteret (kræver regenerering af API-nøgle)
- Overvåg fejlede signaturer – gentagne fejl kan indikere angreb
- Per-bruger-isolering – hver API-nøgle har sin egen HMAC-hemmelighed, så kompromittering af én påvirker ikke de andre
Referencer
- HMAC-signaturguide til klienter
- Hurtig start-guide
- OWASP: Cryptographic Storage Cheat Sheet
- PHP: hash_hmac()