# Sécurité (/fr/webhooks/security)

<!-- agent-signals: reading_time_min: 3 · est_tokens: 1391 · updated: 2026-07-30 -->
Related: [Vue d’ensemble](/fr/webhooks/overview.md), [Types d’événements](/fr/webhooks/events.md), [Tests](/fr/webhooks/testing.md)

Vérifiez que chaque requête de webhook provient bien de Firecrawl en vérifiant sa signature HMAC-SHA256. Cela empêche les attaquants d’usurper des payloads et vous permet d’avoir confiance dans les données avant d’agir en conséquence.

<div id="secret-key">
  ## Clé secrète [#clé-secrète]
</div>

Le secret de votre webhook est disponible dans l’[onglet Avancé](https://www.firecrawl.dev/app/settings?tab=advanced) des paramètres de votre compte. Chaque compte dispose d’un secret unique utilisé pour signer toutes les requêtes webhook.

<Warning>
  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.
</Warning>

<div id="signature-verification">
  ## Vérification de la signature [#vérification-de-la-signature]
</div>

Chaque requête de webhook inclut un en-tête `X-Firecrawl-Signature` :

```
X-Firecrawl-Signature: sha256=abc123def456...
```

<div id="how-to-verify">
  ### Comment vérifier [#comment-vérifier]
</div>

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

<div id="implementation">
  ### Implémentation [#implémentation]
</div>

<CodeGroup>
  <CodeBlockTabs defaultValue="Node/Express" groupId="node-express+python-flask">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Node/Express">
        Node/Express
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python/Flask">
        Python/Flask
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Node/Express">
      ```js  
      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'));
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python/Flask">
      ```python  
      import hmac
      import hashlib
      from flask import Flask, request, abort

      app = Flask(__name__)

      WEBHOOK_SECRET = 'your-webhook-secret-here'  # À récupérer dans le tableau de bord Firecrawl

      @app.post('/webhook/firecrawl')
      def webhook():
          signature = request.headers.get('X-Firecrawl-Signature')
          
          if not signature:
              abort(401, 'En-tête de signature manquant')
          
          # Extraire le hachage de l’en-tête de signature
          try:
              algorithm, hash_value = signature.split('=', 1)
              if algorithm != 'sha256':
                  abort(401, 'Algorithme de signature non valide')
          except ValueError:
              abort(401, 'Format de signature non valide')
          
          # Calculer la signature attendue
          expected_signature = hmac.new(
              WEBHOOK_SECRET.encode('utf-8'),
              request.data,
              hashlib.sha256
          ).hexdigest()
          
          # Vérifier la signature avec une comparaison à l’épreuve du timing
          if not hmac.compare_digest(hash_value, expected_signature):
              abort(401, 'Signature non valide')
          
          # Analyser et traiter le webhook vérifié
          event = request.get_json(force=True)
          print('Webhook Firecrawl vérifié :', event)
          
          return 'ok', 200

      if __name__ == '__main__':
          app.run(port=3000)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="best-practices">
  ## Bonnes pratiques [#bonnes-pratiques]
</div>

* **Vérifiez chaque requête.** Vérifiez toujours la signature avant de traiter un payload de webhook. Rejetez toute requête qui échoue à la vérification avec un code d’état `401`.
* **Utilisez des comparaisons à durée constante.** La comparaison de chaînes standard peut révéler des informations temporelles. Utilisez `crypto.timingSafeEqual()` dans Node.js ou `hmac.compare_digest()` en Python.
* **Exposez votre point de terminaison en HTTPS.** Cela garantit que les payloads de webhook sont chiffrés pendant le transit.
