Webhook-Signaturen prüfen
Jede Zustellung ist nach dem Standard-Webhooks-Schema signiert. So prüfst du Echtheit und Aktualität in Node.js und PHP.
Mosaio signiert jede Zustellung nach dem Standard-Webhooks-Schema. Prüfe die Signatur immer, bevor du Daten verarbeitest – sonst könnte jeder deinen Endpunkt mit gefälschten Ereignissen ansprechen.
Header
| Header | Inhalt |
|---|---|
webhook-id | Eindeutige ID der Zustellung – bleibt bei Wiederholungen gleich |
webhook-timestamp | Unix-Zeit in Sekunden |
webhook-signature | v1, gefolgt von der Base64-Signatur |
Algorithmus
Nimm das Secret (
whsec_…), entferne das Präfixwhsec_und dekodiere den Rest aus Base64. Das ist der HMAC-Schlüssel.Bilde den signierten Inhalt:
${webhook-id}.${webhook-timestamp}.${roher Body}.Berechne HMAC-SHA256 und kodiere das Ergebnis als Base64.
Vergleiche zeitkonstant mit dem Wert nach
v1,.Lehne Zustellungen ab, deren Zeitstempel mehr als fünf Minuten abweicht.
Verwende den rohen Request-Body. Wurde er schon als JSON geparst und neu serialisiert, stimmt die Signatur nicht mehr.
Node.js
import crypto from 'node:crypto'
export function verifyMosaioWebhook(rawBody, headers, secret) {
const id = headers['webhook-id']
const timestamp = headers['webhook-timestamp']
const signature = headers['webhook-signature'] ?? ''
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64')
const expected = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.${rawBody}`)
.digest('base64')
return signature.split(' ').some((part) => {
const [version, sig] = part.split(',')
return version === 'v1'
&& sig?.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
})
}PHP
function verifyMosaioWebhook(string $rawBody, array $headers, string $secret): bool {
$id = $headers['webhook-id'] ?? '';
$timestamp = $headers['webhook-timestamp'] ?? '0';
$signature = $headers['webhook-signature'] ?? '';
if (abs(time() - (int) $timestamp) > 300) return false;
$key = base64_decode(preg_replace('/^whsec_/', '', $secret));
$expected = base64_encode(hash_hmac('sha256', "$id.$timestamp.$rawBody", $key, true));
foreach (explode(' ', $signature) as $part) {
[$version, $sig] = array_pad(explode(',', $part, 2), 2, '');
if ($version === 'v1' && hash_equals($expected, $sig)) return true;
}
return false;
}Weil das Schema dem offenen Standard-Webhooks-Format folgt, funktionieren auch die offiziellen Standard-Webhooks-Bibliotheken für viele Sprachen.
Unser Support antwortet in der Regel innerhalb eines Werktags.