Validation HMAC
La validation HMAC est la méthode utilisée par FastOG pour vérifier que chaque requête d'image OG basée sur un abonnement provient bien de votre propre serveur — les URL signées protègent vos crédits. Inscrivez-vous, puis choisissez un plan Dynamic API et créez votre clé API et votre secret HMAC dans le tableau de bord ; les rendus API payants sont propres. Cette page couvre les règles côté serveur derrière les images Open Graph dynamiques, de la signature canonique des requêtes à la comparaison en temps constant.
Aperçu
La validation de signature HMAC-SHA256 a été ajoutée au point de terminaison /api/v1/og afin d’empêcher toute utilisation non autorisée du quota API. Cela garantit que seules les requêtes provenant du site web légitime peuvent générer des images OG.
Architecture
Authentification à deux niveaux :
- Signature HMAC (premier niveau) — Vérifie que la requête provient d'une source de confiance (le site web)
- Clé API (second niveau) — Identifie le compte utilisateur pour la comptabilisation du quota de rendu
Middleware unique (ApiKeyAuth) : vérifie la signature (paramètres de requête) et résout la clé API pour la comptabilisation du quota de rendu en une seule passe — échec rapide en cas de signature invalide.
Paramètres de requête, pas d'en-têtes : étant donné que les images OG sont récupérées par les robots des réseaux sociaux (LinkedIn, Twitter, Facebook) qui n'envoient pas d'en-têtes personnalisés, la signature est transmise dans les paramètres de requête de l'URL :
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implémentation
Fichiers créés/modifiés
app/Http/Middleware/HmacSignature.php- Nouveau middleware validant les signatures HMAC (paramètres de requête)routes/api.php- Ajout du middleware HMAC à la route OGbootstrap/app.php- Enregistrement de l'alias du middlewarehmacapp/Models/ApiKey.php- Ajout du champhmac_secret(secrets par utilisateur)app/Http/Controllers/DashboardController.php- Génère le secret HMAC lors de la création de la clé APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migration pour la colonne hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte- Affiche le secret HMAC à l'utilisateur (une seule fois)docs/hmac-signature-guide.md- Guide d'implémentation côté clienttests/Feature/HmacSignatureTest.php- 8 tests réussis
Algorithme de signature
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 fait partie de la requête canonique, la signature est donc liée à la clé API. Toute modification d'un paramètre (titre, template, image, …) ou de la clé elle-même modifie la requête canonique → non-concordance de signature → 401 → aucun rendu, aucun quota consommé. Des paramètres identiques → une signature identique → une URL identique → mise en cache en périphérie pour toujours, rejouée sans comptage.
Fonctionnalités de sécurité
- Signature adressée par contenu - Toute modification d'un paramètre → 401 → aucun rendu, aucun quota consommé (l'URL elle-même est la preuve)
- Pas d'horodatage - Les URL sont intégrées dans le HTML public et mises en cache pour toujours ; elles doivent survivre au-delà de 5 minutes
- Comparaison en temps constant (
hash_equals()) - Prévient les attaques temporelles - Clé API dans le message - La signature est liée à la clé ; changer de clé échoue
- Spécifique au chemin - La signature n'est valide que pour le chemin exact
- Spécifique à la méthode - Une signature GET ne fonctionne pas pour POST
- Secrets par utilisateur - Chaque clé API possède son propre secret HMAC, révocable indépendamment (rotation = interrupteur d'arrêt pour les URL divulguées)
Configuration
Générer un secret (automatique)
Le secret HMAC est généré automatiquement lors de la création d’une clé API dans le tableau de bord. Il est affiché une seule fois avec la clé API, puis n’est plus jamais montré.
Génération manuelle (si nécessaire) :
openssl rand -hex 32
Schéma de base de données
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Variables d’environnement
Aucune variable d’environnement globale n’est requise — les secrets sont stockés par utilisateur dans la table api_keys.
Exemple d’utilisation (backend du site web)
// 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éponses d’erreur
| Statut | Code | Description |
|---|---|---|
| 401 | missing_signature |
Paramètre de requête key ou s manquant |
| 401 | invalid_signature |
La signature ne correspond pas à la requête canonique (altérée ou secret incorrect) |
Tests
Exécutez les tests :
php artisan test --filter HmacSignatureTest
Les 8 tests passent tous :
- ✅ Une requête sans signature est rejetée
- ✅ Une requête avec un paramètre de clé manquant est rejetée
- ✅ Un paramètre de contenu modifié (titre changé) est rejeté
- ✅ Un paramètre ajouté est rejeté
- ✅ Une signature invalide est rejetée
- ✅ Une signature valide passe le contrôle HMAC
- ✅ La signature est spécifique au chemin
- ✅ La signature est spécifique à la méthode
- ✅ Des clés API différentes ont des secrets différents
Impact sur les performances
- Négligeable - Le calcul HMAC-SHA256 prend environ 0,1 ms par requête
- Aucun appel réseau supplémentaire - La validation est locale
- Échec rapide - Les signatures invalides sont rejetées avant la recherche de la clé API
Considérations de sécurité
- Ne jamais exposer le secret dans le code côté client — Signez uniquement côté serveur
- Utilisez HTTPS — HMAC protège contre les requêtes non autorisées, HTTPS protège contre les attaques de l’homme du milieu (MITM)
- Faites pivoter les secrets périodiquement — Surtout en cas de compromission (nécessite la régénération de la clé API)
- Surveillez les signatures échouées — Des échecs répétés peuvent indiquer des attaques
- Isolation par utilisateur — Chaque clé API possède son propre secret HMAC ; la compromission de l’une n’affecte pas les autres
Références
- Guide de signature HMAC pour les clients
- Guide de démarrage rapide
- OWASP : Aide-mémoire sur le stockage cryptographique
- PHP : hash_hmac()