✍️ Signature de l'en-tête
Nous signons les événements du webhook en incluant une signature dans l'en-tête Tomorro_Signature
de chaque événement.
Chaque webhook a un secret automatiquement généré et partagé entre Tomorro et l'utilisateur final. Ensuite, pour signer l'événement, nous calculons un HMAC-SHA256
de l'horodatage et du corps de l'événement :
sha256 = HMAC_SHA256(timestamp + "." + eventBody)
Enfin, l'en-tête Tomorro_Signature a le format suivant :
Tomorro-Signature: t=1492774577, sha256=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
avec t
l'horodatage et sha256
la clef précédemment calculé.
Notez que les sauts de lignes ont été ajoutés pour plus de clarté, mais un vrai en-tête Tomorro_Signature
est sur une seule ligne.
🔍 Vérification de la signature
Quand un client reçoit un événement webhook, il doit vérifier que la signature est valide. Voici un exemple de la façon de le faire :
import {createHmac, timingSageEqual} from 'crypto';
function checkSignature(signature, secret, body)
{ const timestamp = signature[0].split('=')[1];
const sig = Buffer.from(signature[1].split('=')[1], 'utf8');
const hmac = createHmac('sha256', secret);
const digest = Buffer.from(hmac.update(timestamp + '.' + JSON.stringify(body)).digest('hex'), 'utf8');
if (sig.length !== digest.length || !timingSafeEqual(digest, sig))
{ return false; }
else { return true; }
}
Notez qu'il est recommandé d'utiliser une comparaison de chaînes de caractères constante dans le temps pour éviter les vulnérabilités liées aux attaques de synchronisation, c'est pourquoi nous utilisons ici timingSafeEqual
au lieu de ===
Par ailleurs, le client doit vérifier si la différence entre l'horodatage reçu et l'horodatage courant est dans sa tolérance (ceci afin d'éviter les attaques par rejeu) .