HMAC-Validierung
Die HMAC-Validierung ist die Methode, mit der FastOG überprüft, dass jede abonnementbasierte OG-Bildanfrage von Ihrem eigenen Server stammt – signierte URLs schützen Ihre Credits. Registrieren Sie sich, wählen Sie dann einen Dynamic-API-Plan und erstellen Sie Ihren API-Schlüssel sowie Ihr HMAC-Geheimnis im Dashboard; kostenpflichtige API-Renderings sind sauber. Diese Seite behandelt die serverseitigen Regeln hinter dynamischen Open-Graph-Bildern, von der kanonischen Query-Signierung bis zum Vergleich in konstanter Zeit.
Übersicht
Die Validierung der HMAC-SHA256-Signatur wurde zum /api/v1/og-Endpunkt hinzugefügt, um eine unbefugte Nutzung des API-Kontingents zu verhindern. Dadurch wird sichergestellt, dass nur Anfragen von der legitimen Website OG-Bilder generieren können.
Architektur
Zweistufige Authentifizierung:
- HMAC-Signatur (zuerst) – Bestätigt, dass die Anfrage von einer vertrauenswürdigen Quelle (der Website) stammt
- API-Schlüssel (danach) – Identifiziert das Benutzerkonto für die Abrechnung des Render-Kontingents
Ein einzelnes Middleware-Modul (ApiKeyAuth): überprüft die Signatur (Query-Parameter) und löst den API-Schlüssel für die Abrechnung des Render-Kontingents in einem Durchgang auf – bei ungültigen Signaturen erfolgt ein schneller Abbruch (Fail Fast).
Query-Parameter statt Header: Da OG-Bilder von Social-Media-Crawlern (LinkedIn, Twitter, Facebook) abgerufen werden, die keine benutzerdefinierten Header senden, wird die Signatur in den URL-Query-Parametern übergeben:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementierung
Erstellte/Geänderte Dateien
app/Http/Middleware/HmacSignature.php– Neue Middleware zur Validierung von HMAC-Signaturen (Query-Parameter)routes/api.php– HMAC-Middleware zur OG-Route hinzugefügtbootstrap/app.php–hmac-Middleware-Alias registriertapp/Models/ApiKey.php–hmac_secret-Feld hinzugefügt (Geheimnisse pro Benutzer)app/Http/Controllers/DashboardController.php– Generiert HMAC-Geheimnis bei Erstellung des API-Schlüsselsdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php– Migration für die hmac_secret-Spalteresources/js/Pages/Dashboard/ApiKeys.svelte– Zeigt HMAC-Geheimnis dem Benutzer an (einmalig)docs/hmac-signature-guide.md– Leitfaden zur Client-Implementierungtests/Feature/HmacSignatureTest.php– 8 erfolgreiche Tests
Signaturalgorithmus
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 ist Teil der kanonischen Query, sodass die Signatur an den API-Schlüssel gebunden ist. Das Ändern eines beliebigen Parameters (Titel, Vorlage, Bild usw.) oder des Schlüssels selbst ändert die kanonische Query → Signatur stimmt nicht überein → 401 → kein Rendering, keine Quota-Zählung. Identische Parameter → identische Signatur → identische URL → dauerhaft am Edge cachebar, ohne erneute Zählung bei Wiederholung.
Sicherheitsfunktionen
- Inhaltsadressierte Signatur – Jede Parameteränderung → 401 → kein Rendering, keine Quota-Zählung (die URL selbst ist der Nachweis)
- Kein Zeitstempel – URLs werden in öffentlichem HTML eingebettet und dauerhaft gecacht; sie müssen länger als 5 Minuten gültig bleiben
- Konstantzeit-Vergleich (
hash_equals()) – Verhindert Timing-Angriffe - API-Schlüssel in der Nachricht – Die Signatur ist an den Schlüssel gebunden; das Austauschen von Schlüsseln schlägt fehl
- Pfadspezifisch – Die Signatur ist nur für den exakten Pfad gültig
- Methodenspezifisch – Eine GET-Signatur funktioniert nicht für POST
- Geheimnisse pro Benutzer – Jeder API-Schlüssel hat sein eigenes HMAC-Geheimnis und kann unabhängig widerrufen werden (Rotation = Kill-Switch für geleakte URLs)
Konfiguration
Geheimschlüssel generieren (automatisch)
Der HMAC-Geheimschlüssel wird automatisch generiert, wenn Sie einen API-Schlüssel im Dashboard erstellen. Er wird einmalig zusammen mit dem API-Schlüssel angezeigt und danach nie wieder.
Manuelle Generierung (falls erforderlich):
openssl rand -hex 32
Datenbankschema
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Umgebungsvariablen
Keine globale Umgebungsvariable erforderlich – Geheimschlüssel werden pro Benutzer in der api_keys-Tabelle gespeichert.
Verwendungsbeispiel (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}" />
Fehlerantworten
| Status | Code | Beschreibung |
|---|---|---|
| 401 | missing_signature |
Fehlender key- oder s-Abfrageparameter |
| 401 | invalid_signature |
Signatur stimmt nicht mit der kanonischen Abfrage überein (manipuliert oder falsches Secret) |
Testen
Tests ausführen:
php artisan test --filter HmacSignatureTest
Alle 8 Tests bestehen:
- ✅ Anfrage ohne Signatur wird abgelehnt
- ✅ Anfrage mit fehlendem Schlüsselparameter wird abgelehnt
- ✅ Manipulierter Inhaltsparameter (Titel geändert) wird abgelehnt
- ✅ Hinzugefügter Parameter wird abgelehnt
- ✅ Ungültige Signatur wird abgelehnt
- ✅ Gültige Signatur besteht die HMAC-Prüfung
- ✅ Signatur ist pfadspezifisch
- ✅ Signatur ist methodspezifisch
- ✅ Verschiedene API-Schlüssel haben unterschiedliche Geheimnisse
Auswirkungen auf die Leistung
- Vernachlässigbar – Die HMAC-SHA256-Berechnung dauert etwa 0,1 ms pro Anfrage
- Keine zusätzlichen Netzwerkaufrufe – Die Validierung erfolgt lokal
- Schnelles Scheitern – Ungültige Signaturen werden abgelehnt, bevor die API-Schlüsselsuche erfolgt
Sicherheitshinweise
- Geben Sie das Geheimnis niemals im clientseitigen Code preis – Signieren Sie ausschließlich auf dem Server
- Verwenden Sie HTTPS – HMAC schützt vor unbefugten Anfragen, HTTPS schützt vor Man-in-the-Middle-Angriffen
- Rotieren Sie Geheimnisse regelmäßig – Insbesondere bei Kompromittierung (erfordert die Neugenerierung des API-Schlüssels)
- Überwachen Sie fehlgeschlagene Signaturen – Wiederholte Fehlschläge können auf Angriffe hindeuten
- Isolation pro Benutzer – Jeder API-Schlüssel besitzt sein eigenes HMAC-Geheimnis; die Kompromittierung eines Schlüssels hat keine Auswirkungen auf andere
Referenzen
- HMAC-Signatur-Leitfaden für Clients
- Schnellstart-Anleitung
- OWASP: Cryptographic Storage Cheat Sheet
- PHP: hash_hmac()