Localisé par IA

Signature de requête

Chaque requête webhook d'Emailit inclut un en-tête X-Emailit-Signature contenant une signature HMAC-SHA256 et un en-tête X-Emailit-Timestamp avec l'horodatage Unix du moment où la requête a été signée. Vous devez vérifier cette signature pour vous assurer que la requête est authentique et n'a pas été altérée.

Fonctionnement

  1. Lorsque vous créez un webhook, Emailit génère un secret de signature pour ce point de terminaison
  2. Pour chaque livraison, Emailit concatène l'horodatage et le corps brut de la requête sous la forme {timestamp}.{rawBody}
  3. Un hachage HMAC-SHA256 est calculé sur cette chaîne en utilisant votre secret de signature
  4. La signature est envoyée dans l'en-tête X-Emailit-Signature et l'horodatage dans X-Emailit-Timestamp
  5. Votre serveur recalcule la signature et la compare à la valeur de l'en-tête

Vérification de la signature

Pour vérifier une signature de webhook :

  1. Extrayez les en-têtes X-Emailit-Signature et X-Emailit-Timestamp de la requête
  2. Concaténez l'horodatage et le corps brut de la requête sous la forme {timestamp}.{rawBody}
  3. Calculez un hachage HMAC-SHA256 de cette chaîne en utilisant votre secret de signature webhook
  4. Comparez le hachage calculé avec la signature en utilisant une comparaison sécurisée temporellement
  5. Optionnellement, rejetez les requêtes dont l'horodatage est antérieur de quelques minutes pour se protéger contre les attaques par rejeu

Vérifier la signature

import crypto from 'crypto';

function verifyWebhookSignature(rawBody, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${rawBody}`;
  const computed = crypto
    .createHmac('sha256', secret)
    .update(signedPayload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computed, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

// Dans votre gestionnaire de webhook :
const rawBody = req.body; // corps brut de la requête sous forme de chaîne
const signature = req.headers['x-emailit-signature'];
const timestamp = req.headers['x-emailit-timestamp'];
const secret = process.env.WEBHOOK_SIGNING_SECRET;

// Protection contre les attaques par rejeu (tolérance de 5 minutes)
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > 300) {
  return res.status(401).send('Requête trop ancienne');
}

if (!verifyWebhookSignature(rawBody, signature, timestamp, secret)) {
  return res.status(401).send('Signature invalide');
}

Points importants

  • La signature est calculée sur {timestamp}.{rawBody} — utilisez toujours la chaîne du corps brut de la requête, pas une version re-sérialisée (par ex. JSON.stringify(req.body)). Les différences d'espacement ou d'ordre des clés casseront la signature.
  • Utilisez toujours une fonction de comparaison sécurisée temporellement (par ex. crypto.timingSafeEqual, hmac.compare_digest, hash_equals) pour prévenir les attaques temporelles.
  • Envisagez de rejeter les requêtes dont le X-Emailit-Timestamp est antérieur de quelques minutes pour se protéger contre les attaques par rejeu.
  • Gardez votre secret de signature en sécurité — stockez-le dans des variables d'environnement, pas dans le code source.
  • Vous pouvez trouver votre secret de signature webhook dans le tableau de bord Emailit sous les paramètres de webhook, ou via l'API Webhooks.