# Segurança (/pt-BR/webhooks/security)

<!-- agent-signals: reading_time_min: 3 · est_tokens: 1380 · updated: 2026-07-30 -->
Related: [Visão geral](/pt-BR/webhooks/overview.md), [Tipos de eventos](/pt-BR/webhooks/events.md), [Testes](/pt-BR/webhooks/testing.md)

Verifique se cada requisição de webhook realmente veio do Firecrawl conferindo sua assinatura HMAC-SHA256. Isso impede que invasores falsifiquem payloads e permite que você confie nos dados antes de agir com base neles.

<div id="secret-key">
  ## Chave secreta [#chave-secreta]
</div>

Seu segredo de webhook está disponível na [aba Advanced](https://www.firecrawl.dev/app/settings?tab=advanced) das configurações da sua conta. Cada conta tem um segredo exclusivo usado para assinar todas as solicitações de webhook.

<Warning>
  Mantenha seu segredo de webhook em segurança e nunca o exponha publicamente. Se você acreditar que seu segredo foi comprometido, gere um novo imediatamente nas configurações da sua conta.
</Warning>

<div id="signature-verification">
  ## Verificação de assinatura [#verificação-de-assinatura]
</div>

Cada requisição de webhook inclui um cabeçalho `X-Firecrawl-Signature`:

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

<div id="how-to-verify">
  ### Como verificar [#como-verificar]
</div>

1. Extraia a assinatura do cabeçalho `X-Firecrawl-Signature`
2. Obtenha o corpo bruto da requisição exatamente como recebido (não faça parsing antes)
3. Calcule o HMAC-SHA256 usando sua chave secreta
4. Compare as assinaturas usando uma função de comparação segura a timing (timing-safe)

<div id="implementation">
  ### Implementação [#implementação]
</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();

      // Use o parser de corpo bruto para verificar a assinatura
      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('Não autorizado');
        }
        
        // Extraia o hash do cabeçalho de assinatura
        const [algorithm, hash] = signature.split('=');
        if (algorithm !== 'sha256') {
          return res.status(401).send('Algoritmo de assinatura inválido');
        }
        
        // Calcule a assinatura esperada
        const expectedSignature = crypto
          .createHmac('sha256', webhookSecret)
          .update(req.body)
          .digest('hex');
        
        // Verifique a assinatura usando comparação com segurança temporal
        if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(expectedSignature, 'hex'))) {
          return res.status(401).send('Assinatura inválida');
        }
        
        // Analise e processe o webhook verificado
        const event = JSON.parse(req.body);
        console.log('Webhook do Firecrawl verificado:', event);
        
        res.status(200).send('ok');
      });

      app.listen(3000, () => console.log('Escutando na porta 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'  # Obtenha no dashboard do Firecrawl

      @app.post('/webhook/firecrawl')
      def webhook():
          signature = request.headers.get('X-Firecrawl-Signature')
          
          if not signature:
              abort(401, 'Cabeçalho de assinatura ausente')
          
          # Extrair o hash do cabeçalho de assinatura
          try:
              algorithm, hash_value = signature.split('=', 1)
              if algorithm != 'sha256':
                  abort(401, 'Algoritmo de assinatura inválido')
          except ValueError:
              abort(401, 'Formato de assinatura inválido')
          
          # Calcular a assinatura esperada
          expected_signature = hmac.new(
              WEBHOOK_SECRET.encode('utf-8'),
              request.data,
              hashlib.sha256
          ).hexdigest()
          
          # Verificar a assinatura usando comparação protegida contra análise de tempo
          if not hmac.compare_digest(hash_value, expected_signature):
              abort(401, 'Assinatura inválida')
          
          # Analisar e processar o webhook verificado
          event = request.get_json(force=True)
          print('Webhook verificado do Firecrawl:', event)
          
          return 'ok', 200

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

<div id="best-practices">
  ## Boas práticas [#boas-práticas]
</div>

* **Verifique cada requisição.** Sempre verifique a assinatura antes de processar um payload de webhook. Rejeite qualquer requisição que falhe na verificação com status `401`.
* **Use comparações seguras a timing.** Comparações padrão de strings podem revelar informações por tempo de execução. Use `crypto.timingSafeEqual()` no Node.js ou `hmac.compare_digest()` no Python.
* **Disponibilize seu endpoint via HTTPS.** Isso garante que os payloads de webhook sejam criptografados em trânsito.
