Quando você expõe uma rota pública de webhook para receber eventos da WhatsApp Cloud API, está abrindo uma porta de entrada para dados externos.
Essa porta é necessária para atendimento em tempo real. Mas, sem validação, ela também pode receber requisições forjadas que simulam mensagens de clientes, alteram status de conversa ou tentam contaminar sua automação com dados falsos.
A validação da assinatura confirma se cada payload recebido no endpoint veio dos servidores da Meta e se o corpo da requisição não foi alterado no caminho.
O que é X-Hub-Signature-256?
Sempre que a Meta dispara um evento de webhook via POST, ela envia um cabeçalho HTTP chamado X-Hub-Signature-256.
Esse cabeçalho contém uma assinatura criptográfica gerada com HMAC-SHA256. Na prática, a Meta combina duas informações:
- O corpo bruto da requisição: o JSON exatamente como foi enviado, sem parse, formatação ou alteração.
- O App Secret: o segredo do aplicativo configurado no Meta for Developers, conhecido apenas pela sua aplicação e pela Meta.
Como o App Secret é privado, um invasor não consegue gerar uma assinatura válida para um payload modificado. Ele pode até enviar JSON para o seu endpoint. O que ele não consegue é provar que aquele JSON foi assinado com a chave correta.
Nunca exponha o App Secret em frontend, logs, repositórios ou mensagens de erro. Ele deve viver em variável de ambiente ou cofre de segredos.
O fluxo da validação
A lógica de segurança é curta. O seu servidor recebe a requisição, calcula a assinatura localmente e compara o resultado com o cabeçalho enviado pela Meta.
- Captura: leia o payload bruto da requisição e o valor do cabeçalho
X-Hub-Signature-256. - Cálculo local: gere um hash HMAC-SHA256 do raw body usando o seu App Secret como chave.
- Comparação estrita: se o hash local for igual ao hash recebido, processe o evento. Se não for, responda
403 Forbidden.
Esse protocolo funciona como um aperto de mão criptográfico. A Meta assina o conteúdo; sua aplicação recalcula a assinatura; apenas payloads consistentes passam.
Implementando em PHP
O exemplo abaixo usa PHP puro. Em Laravel, Slim, Symfony ou outro framework, a ideia é a mesma: capturar o corpo bruto antes de qualquer transformação e comparar com segurança.
Dois detalhes importam nesse código. Primeiro, a comparação usa hash_equals(), que reduz risco de ataques de temporização. Segundo, o cálculo usa php://input, que representa o corpo bruto recebido pela aplicação. Valide a assinatura antes de gravar, parsear ou acionar qualquer automação. O endpoint deve recusar tráfego inválido o mais cedo possível.
O que bloquear em produção?
Nem toda falha deve ser tratada da mesma forma, mas todas devem interromper o processamento do evento. Uma assinatura ausente, malformada ou incompatível indica que a requisição não pode ser considerada confiável.
| Cenário | Resposta recomendada | Motivo | Status | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Cabeçalho ausente | Bloquear | Não há prova criptográfica da origem | 401 | no Formato diferente de sha256= | Bloquear | Cabeçalho inesperado ou manipulado | 403 | no Hash incompatível | Bloquear | Payload pode ter sido forjado ou alterado | 403 | no Hash válido | Aceitar e enfileirar | Evento passou pela validação | 200 |
Além do status HTTP, registre logs estruturados. Guarde horário, IP de origem, motivo do bloqueio e identificador do endpoint. Evite registrar o payload completo quando houver dados sensíveis de clientes.
Três cuidados críticos com o payload
Os bugs mais comuns nessa implementação não estão na função de hash. Eles aparecem na forma como o framework entrega a requisição para o seu código.
Use sempre o raw body
Nunca calcule o hash usando um objeto JSON já parseado. Mudanças pequenas de espaçamento, ordem, encoding ou quebra de linha invalidam a assinatura. A assinatura precisa ser calculada sobre os bytes originais da requisição.
Use comparação segura
Evite operadores como == ou === para comparar assinaturas. Use funções nativas preparadas para esse cenário, como hash_equals() no PHP ou crypto.timingSafeEqual() no Node.js.
Responda rápido
A validação deve acontecer imediatamente. Depois de aprovar o hash, grave o evento em uma fila assíncrona, como Redis ou RabbitMQ, e responda 200 OK rapidamente. Isso reduz retries automáticos e evita travar o endpoint com processamento pesado.
Checklist de segurança
Antes de liberar o webhook em produção, revise a implementação como parte da arquitetura de segurança da integração.
- App Secret em variável segura: sem segredo hardcoded no código.
- HTTPS obrigatório: nunca receba webhooks em endpoints sem TLS.
- Validação antes do parse: nenhum processamento antes da assinatura ser aceita.
- Logs sem dados sensíveis: registre o suficiente para auditoria, sem expor conteúdo de clientes.
- Fila para processamento: o endpoint responde rápido e deixa regras de negócio para workers.
- Alertas de falha: picos de
403podem indicar ataque, configuração errada ou segredo desatualizado.
Conclusão
Validar a assinatura do webhook da Meta é uma camada básica para qualquer aplicação séria conectada à WhatsApp Cloud API. Ela protege sua infraestrutura contra payloads falsos e preserva a integridade dos dados que alimentam chatbots, filas, CRM e relatórios.
Segurança, nesse caso, não é um detalhe técnico isolado. É o alicerce para automatizar atendimento sem aceitar eventos que a sua aplicação não consegue confiar.
Procura uma infraestrutura de atendimento que cuide de criptografia, segurança de dados e alta disponibilidade de webhooks de forma nativa? Conecte seu ecossistema à Atys e reduza a complexidade dos bastidores da sua operação.
Comentários
0 comentários