La sécurité des webhooks est essentielle pour garantir que les requêtes adressées à votre endpoint proviennent bien de Firecrawl et n’ont pas été altérées. Cette page explique comment vérifier l’authenticité des webhooks et appliquer les bonnes pratiques de sécurité.

Pourquoi la sécurité des webhooks est essentielle

Sans vérification adéquate, des attaquants pourraient :
  • Envoyer de fausses requêtes de webhook pour déclencher des actions indésirables
  • Modifier le contenu du payload pour manipuler votre application
  • Saturer votre point de terminaison de webhook avec des requêtes

Comment Firecrawl signe les webhooks

Firecrawl signe chaque requête de webhook à l’aide d’un HMAC-SHA256 calculé avec la clé secrète de votre compte. Cela génère une signature unique pour chaque requête, qui prouve :
  1. Que la requête provient bien de Firecrawl
  2. Que le contenu (payload) n’a pas été modifié

Rechercher votre clé secrète

Le secret de votre webhook est disponible dans l’onglet Avancé des paramètres de votre compte. Chaque compte dispose d’un secret unique utilisé pour signer toutes les requêtes webhook.
Gardez le secret de votre webhook en lieu sûr et ne l’exposez jamais publiquement. Si vous pensez que votre secret a été compromis, régénérez-le immédiatement depuis les paramètres de votre compte.

Vérification de la signature

Fonctionnement des signatures

Chaque requête de webhook inclut un en-tête X-Firecrawl-Signature au format suivant :
X-Firecrawl-Signature: sha256=abc123def456...
La signature est calculée comme suit :
  1. Prendre le corps brut de la requête (chaîne JSON)
  2. Créer un hachage HMAC-SHA256 à l’aide de votre clé secrète
  3. Convertir en chaîne hexadécimale
  4. Préfixer avec sha256=

Exemples d’implémentation

import crypto from 'crypto';
import express from 'express';

const app = express();

// Utiliser un parseur de corps brut pour vérifier la signature
app.use('/webhook/firecrawl', express.raw({ type: 'application/json' }));

app.post('/webhook/firecrawl', (req, res) => {
  const signature = req.get('X-Firecrawl-Signature');
  const webhookSecret = process.env.FIRECRAWL_WEBHOOK_SECRET;
  
  if (!signature || !webhookSecret) {
    return res.status(401).send('Non autorisé');
  }
  
  // Extract hash from signature header
  // Extraire le hash de l’en-tête de signature
  if (algorithm !== 'sha256') {
    return res.status(401).send('Algorithme de signature invalide');
  }
  
  // Calculer la signature attendue
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(req.body)
    .digest('hex');
  
  // Vérifier la signature avec une comparaison sûre au timing
  if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(expectedSignature, 'hex'))) {
    return res.status(401).send('Signature invalide');
  }
  
  // Analyser et traiter le webhook vérifié
  const event = JSON.parse(req.body);
  console.log('Webhook Firecrawl vérifié :', event);
  
  res.status(200).send('ok');
});

app.listen(3000, () => console.log('Écoute sur le port 3000'));

Vérification étape par étape

  1. Extraire la signature de l’en-tête X-Firecrawl-Signature
  2. Récupérer le corps brut de la requête tel que reçu (ne le parsez pas au préalable)
  3. Calculer le HMAC-SHA256 avec votre clé secrète et le corps brut
  4. Comparer les signatures à l’aide d’une fonction de comparaison à durée constante
  5. Ne traiter le webhook que si les signatures correspondent

Bonnes pratiques de sécurité

Toujours valider les signatures

Ne faites jamais confiance à une requête de webhook sans vérification de signature :
// ❌ MAUVAIS - Aucune vérification
app.post('/webhook', (req, res) => {
  processWebhook(req.body); // Dangereux !
  res.status(200).send('OK');
});

// ✅ BON - Vérification préalable
app.post('/webhook', (req, res) => {
  if (!verifySignature(req)) {
    return res.status(401).send('Accès non autorisé');
  }
  processWebhook(req.body);
  res.status(200).send('OK');
});

Utilisez des comparaisons à temps constant

La comparaison de chaînes standard peut révéler des informations temporelles. Utilisez des fonctions dédiées :
  • Node.js : crypto.timingSafeEqual()
  • Python : hmac.compare_digest()
  • Autres langages : recherchez des fonctions de comparaison « constant-time » ou « timing-safe »

Exiger HTTPS

Utilisez toujours des points de terminaison HTTPS pour les webhooks :
{
  "url": "https://your-app.com/webhook" // ✅ Sécurisé
}
{
  "url": "http://your-app.com/webhook" // ❌ Non sécurisé
}