Sécurité webhook
Toutes les livraisons webhook HTTP sont signées avec HMAC-SHA256. Vérifiez la signature de votre côté pour confirmer que la requête provient de Mailverick et n'a pas été altérée.
Format de la signature
La signature est envoyée dans l'en-tête de la requête et suit ce format :
t=1711278330,v1=5a4e3c2b1d... t: Horodatage Unix (secondes) de l'envoi du webhookv1: Empreinte HMAC-SHA256 hexadécimale du payload signé
Fonctionnement de la signature
Le payload signé est construit en concaténant l'horodatage et le corps brut de la requête avec un point comme séparateur :
signed_payload = timestamp + "." + raw_body L'empreinte HMAC-SHA256 est calculée en utilisant votre secret webhook comme clé.
Étapes de vérification
- Extraire le
t(horodatage) et lev1(hash) de l'en-tête de signature. - Optionnellement, vérifier que l'horodatage est dans une fenêtre de tolérance (300 secondes par défaut) pour prévenir les attaques par rejeu.
- Construire le payload signé :
t + "." + corps_brut_requête. - Calculer l'empreinte HMAC-SHA256 avec votre secret webhook.
- Comparer le hash calculé avec la valeur
v1en utilisant une comparaison à temps constant.
Exemples de code
import { createHmac, timingSafeEqual } from "crypto";
// toleranceSec: max age in seconds, 0 to skip replay check
function verifyWebhookSignature(payload, signature, secret, toleranceSec = 300) {
const [, ts, hash] = signature.match(/^t=(\d+),v1=([a-f0-9]+)$/) || [];
if (!ts) return false;
if (toleranceSec > 0 && Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
const expected = createHmac("sha256", secret).update(ts + "." + payload).digest("hex");
return timingSafeEqual(Buffer.from(hash), Buffer.from(expected));
} import hmac, hashlib, re, time
# tolerance_sec: max age in seconds, 0 to skip replay check
def verify_webhook_signature(payload, signature, secret, tolerance_sec=300):
m = re.match(r"^t=(\d+),v1=([a-f0-9]+)$", signature)
if not m:
return False
ts, hash_val = m.group(1), m.group(2)
if tolerance_sec > 0 and abs(time.time() - int(ts)) > tolerance_sec:
return False
expected = hmac.new(secret.encode(), f"{ts}.{payload}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(hash_val, expected) import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.HexFormat;
import java.util.regex.*;
public class WebhookSignature {
public static boolean verify(String payload, String signature, String secret, long toleranceSec) {
Matcher m = Pattern.compile("^t=(\\d+),v1=([a-f0-9]+)$").matcher(signature);
if (!m.matches()) return false;
long ts = Long.parseLong(m.group(1));
if (toleranceSec > 0 && Math.abs(System.currentTimeMillis() / 1000 - ts) > toleranceSec) return false;
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
String expected = HexFormat.of().formatHex(mac.doFinal((ts + "." + payload).getBytes("UTF-8")));
return MessageDigest.isEqual(m.group(2).getBytes(), expected.getBytes());
} catch (Exception e) { return false; }
}
} <?php
function verifyWebhookSignature(string $payload, string $signature, string $secret, int $toleranceSec = 300): bool {
if (!preg_match('/^t=(\d+),v1=([a-f0-9]+)$/', $signature, $m)) return false;
[$ts, $hash] = [$m[1], $m[2]];
if ($toleranceSec > 0 && abs(time() - (int)$ts) > $toleranceSec) return false;
$expected = hash_hmac('sha256', "$ts.$payload", $secret);
return hash_equals($hash, $expected);
} Gestion des secrets
Votre secret webhook est généré automatiquement lors de la création d'un abonné. Vous pouvez le régénérer à tout moment depuis le portail via l'action « Régénérer le secret ». Après rotation, l'ancien secret est immédiatement invalidé.
Timeout
Les requêtes webhook ont un timeout configurable entre 5 et 300 secondes. Si votre endpoint ne répond pas dans le délai, la livraison est marquée comme échouée. Les livraisons échouées ne sont pas réessayées. Utilisez une file de messages de votre côté si vous avez besoin d'un traitement garanti.
Intégrations Pub/Sub
Google Cloud Pub/Sub, AWS SNS et Azure Event Grid utilisent leurs propres mécanismes d'authentification (IAM, identifiants de compte de service). Les signatures webhook ne sont pas utilisées pour ces intégrations. L'authentification est gérée par le fournisseur cloud.