> ## Documentation Index
> Fetch the complete documentation index at: https://docs.socialsell.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Segurança e verificação

> Como validar a assinatura HMAC de cada entrega para garantir que veio da SocialSell.

Qualquer pessoa com acesso à URL do seu webhook pode simular uma entrega. Por isso, a SocialSell assina cada requisição com HMAC-SHA256 usando o `secret` da sua assinatura — e você **deve** validar essa assinatura antes de processar qualquer payload.

## O header de assinatura

Cada entrega inclui o header:

```
X-SocialSell-Signature: sha256=a1b2c3d4e5f6789...
```

O valor é `sha256=` seguido do HMAC-SHA256 do corpo bruto da requisição, usando o `secret` da assinatura como chave.

## Como validar

<Warning>
  Sempre use o corpo **bruto** (raw bytes) da requisição, não o JSON parseado. Re-serializar o JSON pode alterar a assinatura.
</Warning>

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  const crypto = require('crypto');
  const express = require('express');
  const app = express();

  // Use express.raw() para receber o corpo como Buffer
  app.post('/webhooks/socialsell', express.raw({ type: 'application/json' }), (req, res) => {
    const signature = req.headers['x-socialsell-signature'];
    const secret = process.env.SOCIALSELL_WEBHOOK_SECRET;

    if (!isValidSignature(req.body, signature, secret)) {
      return res.status(401).json({ error: 'Assinatura inválida' });
    }

    const event = JSON.parse(req.body);
    console.log('Evento recebido:', event.event);

    // processar evento...
    res.status(200).send('OK');
  });

  function isValidSignature(body, signature, secret) {
    const expected = 'sha256=' + crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');

    // timingSafeEqual previne timing attacks
    try {
      return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
      );
    } catch {
      return false;
    }
  }
  ```

  ```python Python (Flask) theme={null}
  import hmac
  import hashlib
  import os
  from flask import Flask, request, abort

  app = Flask(__name__)

  @app.route('/webhooks/socialsell', methods=['POST'])
  def webhook():
      signature = request.headers.get('X-SocialSell-Signature', '')
      secret = os.environ['SOCIALSELL_WEBHOOK_SECRET'].encode()

      if not is_valid_signature(request.data, signature, secret):
          abort(401)

      event = request.json
      print(f"Evento recebido: {event['event']}")

      # processar evento...
      return 'OK', 200

  def is_valid_signature(body, signature, secret):
      expected = 'sha256=' + hmac.new(secret, body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(signature, expected)
  ```

  ```python Python (Django) theme={null}
  import hmac
  import hashlib
  import os
  import json
  from django.http import HttpResponse, HttpResponseForbidden
  from django.views.decorators.csrf import csrf_exempt

  @csrf_exempt
  def webhook(request):
      if request.method != 'POST':
          return HttpResponseForbidden()

      signature = request.headers.get('X-SocialSell-Signature', '')
      secret = os.environ['SOCIALSELL_WEBHOOK_SECRET'].encode()

      expected = 'sha256=' + hmac.new(secret, request.body, hashlib.sha256).hexdigest()

      if not hmac.compare_digest(signature, expected):
          return HttpResponseForbidden('Assinatura inválida')

      event = json.loads(request.body)
      print(f"Evento recebido: {event['event']}")

      return HttpResponse('OK')
  ```

  ```typescript TypeScript (Fastify) theme={null}
  import crypto from 'crypto';
  import Fastify from 'fastify';

  const fastify = Fastify();

  fastify.addContentTypeParser(
    'application/json',
    { parseAs: 'buffer' },
    (req, body, done) => done(null, body)
  );

  fastify.post('/webhooks/socialsell', (request, reply) => {
    const signature = request.headers['x-socialsell-signature'] as string;
    const secret = process.env.SOCIALSELL_WEBHOOK_SECRET!;
    const body = request.body as Buffer;

    const expected = 'sha256=' + crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      return reply.status(401).send({ error: 'Assinatura inválida' });
    }

    const event = JSON.parse(body.toString());
    console.log('Evento recebido:', event.event);

    reply.status(200).send('OK');
  });
  ```
</CodeGroup>

## Erros comuns de validação

| Problema                           | Causa                                   | Solução                                        |
| ---------------------------------- | --------------------------------------- | ---------------------------------------------- |
| Assinatura sempre inválida         | Corpo sendo parseado antes da validação | Use o corpo bruto (raw buffer)                 |
| `timingSafeEqual` lançando erro    | Strings de tamanho diferente            | Confirme que o `secret` está correto           |
| Validação falhando em produção     | `secret` com espaços ou quebra de linha | Verifique a variável de ambiente com `trim()`  |
| Funcionou local, falha em produção | Proxy modificando o corpo               | Configure o proxy para não modificar o payload |

## Boas práticas de segurança

* **Sempre valide antes de processar** — nunca confie no payload sem verificar a assinatura
* **Use `timingSafeEqual`** — previne timing attacks onde um atacante mede o tempo de comparação para adivinhar a assinatura
* **Armazene o `secret` como variável de ambiente** — nunca no código ou em repositórios
* **Rejeite com `401`** — não com `200`. Responder com sucesso a payloads inválidos pode confundir sistemas de monitoramento
* **Rotacione o secret se comprometido** — delete a assinatura e crie uma nova com `POST /v1/webhooks`
