Ver planos
Início/ Blog/ WhatsApp/ Como validar a assinatura do webhook da Meta
WhatsApp

Como validar a assinatura do webhook da Meta

C
Carlos 27 de maio de 2026 · 9 min de leitura
Como validar a assinatura do webhook da Meta
Em resumo

A Meta assina cada webhook no cabeçalho X-Hub-Signature-256. Para validar, calcule um HMAC-SHA256 do corpo bruto da requisição usando o App Secret como chave e compare com hash_equals. Se não bater, responda 403 e não processe nada.

Como validar a assinatura de um webhook da Meta?

A Meta envia em cada requisição o cabeçalho X-Hub-Signature-256, com uma assinatura no formato sha256=hash. Para validar, calcule um HMAC-SHA256 do corpo bruto da requisição usando o App Secret do seu aplicativo como chave e compare os dois valores com uma função de comparação segura, como hash_equals no PHP. Se os hashes não forem idênticos, responda 403 e descarte o evento.

Ao expor uma rota pública para receber eventos da WhatsApp Cloud API, você abre uma porta por onde entram dados de fora. Isso é necessário para atender em tempo real, mas tem um custo se a porta não tiver tranca.

Sem validação, qualquer pessoa que descubra a URL do seu webhook pode enviar requisições forjadas: simular mensagens de clientes que nunca existiram, mudar o status de uma conversa ou alimentar a sua automação com dados falsos. O trabalho de defesa cabe em umas quinze linhas de código.

O que a validação garante: duas coisas ao mesmo tempo. Que o payload saiu mesmo dos servidores da Meta e que o corpo da requisição não foi alterado no caminho até você.

Nº 1
falhas de autorização são o risco mais crítico de APIs segundo o ranking do OWASP, à frente de qualquer vulnerabilidade de código
OWASP, API Security Top 10 (2023)

O que é o X-Hub-Signature-256

Toda vez que a Meta dispara um evento por POST, ela inclui um cabeçalho HTTP chamado X-Hub-Signature-256. Dentro dele vai uma assinatura criptográfica gerada com HMAC-SHA256, no formato sha256= seguido do hash.

Essa assinatura combina duas informações.

  • O corpo bruto da requisição O JSON exatamente como foi enviado, byte a byte, sem passar por leitura, reformatação ou reordenação de campos.
  • O App Secret O segredo do aplicativo configurado no Meta for Developers, conhecido apenas pela sua aplicação e pela própria Meta.

Como o App Secret é privado, quem não o tem não consegue produzir uma assinatura válida para um payload inventado. A pessoa até consegue enviar JSON para o seu endpoint. O que ela não consegue é provar que aquele JSON foi assinado com a chave certa.

Imagine que a Meta lacra cada envelope com um carimbo que só ela e você sabem fazer. Você não precisa confiar no carteiro: basta conferir o carimbo.

O App Secret não pode vazar em lugar nenhum. Nada de front-end, log, repositório ou mensagem de erro. Ele vive em variável de ambiente ou em cofre de segredos, e só isso. Se ele vazar, a assinatura deixa de provar qualquer coisa.

O fluxo, em três passos

A lógica é curta: o servidor recebe a requisição, calcula a assinatura por conta própria e compara com o que veio no cabeçalho.

  1. Capture Leia o payload bruto da requisição e o valor do cabeçalho X-Hub-Signature-256. Nesta etapa nada ainda foi interpretado como JSON.
  2. Calcule localmente Gere um HMAC-SHA256 desse corpo bruto usando o App Secret como chave. O resultado é uma sequência hexadecimal.
  3. Compare com segurança Se o hash calculado for idêntico ao recebido, aceite o evento e coloque na fila. Se não for, responda 403 e pare por ali, sem gravar nem processar nada.

Implementando em PHP

O exemplo abaixo usa PHP puro. Em Laravel, Symfony ou qualquer outro framework a ideia não muda: capture o corpo bruto antes de qualquer transformação e compare com uma função segura.

<?php

$appSecret       = getenv('META_APP_SECRET');
$signatureHeader = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

if (!$appSecret || $signatureHeader === '') {
    http_response_code(401);
    exit('Assinatura ausente.');
}

if (!str_starts_with($signatureHeader, 'sha256=')) {
    http_response_code(403);
    exit('Formato de assinatura invalido.');
}

$receivedHash = substr($signatureHeader, strlen('sha256='));
$rawPayload   = file_get_contents('php://input');

$calculatedHash = hash_hmac('sha256', $rawPayload, $appSecret);

if (!hash_equals($calculatedHash, $receivedHash)) {
    http_response_code(403);
    exit('Assinatura invalida.');
}

// A partir daqui o evento e confiavel.
// Enfileire o processamento e responda imediatamente.

http_response_code(200);
echo 'EVENT_RECEIVED';

Dois detalhes desse código merecem atenção. A comparação usa hash_equals(), que gasta o mesmo tempo independentemente de onde as strings divergem e assim não dá pistas a quem tenta adivinhar o hash. E o cálculo usa php://input, que é o corpo bruto, e não o array já interpretado.

A ordem importa: valide a assinatura antes de gravar, interpretar ou acionar qualquer automação. O endpoint precisa recusar tráfego inválido o mais cedo possível, de preferência antes de tocar no banco de dados.

O que bloquear em produção

Nem toda falha tem a mesma causa, mas todas devem interromper o processamento. Assinatura ausente, malformada ou incompatível significa a mesma coisa: aquela requisição não é confiável.

Cenário Ação Status Por quê
Cabeçalho ausente Bloquear 401 Não existe prova criptográfica da origem
Formato diferente de sha256= Bloquear 403 Cabeçalho inesperado ou manipulado
Hash incompatível Bloquear 403 O payload pode ter sido forjado ou alterado
Hash válido Aceitar e enfileirar 200 O evento passou pela validação

Além do status HTTP, registre um log estruturado de cada bloqueio, com horário, IP de origem, motivo e identificador do endpoint. Evite gravar o payload inteiro quando ele carregar dados de clientes: o log também é um lugar onde dado pessoal vaza. Esse cuidado conversa com o tema das permissões de usuário e dados sensíveis no CRM.

Três erros que quebram a validação

Curiosamente, o bug quase nunca está na função de hash. Ele aparece na forma como o framework entrega a requisição para o seu código.

Calcular o hash sobre o JSON já interpretado

Se você gerar o hash a partir de um objeto que o framework leu e reserializou, qualquer diferença de espaçamento, ordem de chave ou codificação muda o resultado. A assinatura precisa ser calculada sobre os bytes originais, exatamente como chegaram.

Comparar com igualdade comum

Evite == e === para comparar assinaturas. Eles param na primeira diferença, e essa diferença de tempo pode ser medida por quem está tentando descobrir o hash correto. Use hash_equals() no PHP ou crypto.timingSafeEqual() no Node.js.

Processar tudo antes de responder

Depois de aprovar o hash, jogue o evento em uma fila e responda 200 na hora. Se você rodar regra de negócio dentro do endpoint, a Meta interpreta a demora como falha e reentrega o mesmo evento, o que pode duplicar o efeito no seu sistema.

Checklist antes de subir para produção

Vale passar por esta lista uma última vez antes de liberar o endpoint.

  • App Secret fora do código Em variável de ambiente ou cofre de segredos, nunca escrito direto no arquivo nem comitado por engano.
  • HTTPS obrigatório Webhook em endpoint sem TLS não é aceito pela Meta e, mesmo que fosse, exporia o conteúdo das conversas.
  • Validação antes de qualquer processamento Nada de gravar, interpretar ou disparar automação antes de a assinatura ser aceita.
  • Log sem dado sensível Registre o suficiente para auditar e investigar, sem despejar o conteúdo das mensagens dos clientes no arquivo de log.
  • Fila para o processamento O endpoint responde rápido e deixa a regra de negócio para os workers, que podem falhar e tentar de novo sem perder evento.
  • Alerta para picos de 403 Um aumento repentino de bloqueios pode significar tentativa de ataque, configuração errada ou App Secret desatualizado depois de uma rotação.

Como a Atys trata isso por baixo do pano

O problema deste artigo é concreto: quem recebe webhook da Meta precisa manter uma rota pública segura, com segredo guardado, assinatura conferida a cada requisição, log auditado e fila para não perder evento. Isso não é difícil de escrever, mas é fácil de esquecer de manter.

Quem usa a Atys não expõe endpoint nenhum. O webhook fica do lado da plataforma, e a validação de assinatura, a rotação de segredo, o tratamento de reentrega e o monitoramento de falhas já vêm prontos e são mantidos por quem cuida disso o dia inteiro.

Assinatura conferida em toda requisição

Nenhum evento chega à sua operação sem passar pela verificação do HMAC contra o App Secret.

Segredos fora do seu ambiente

Você não guarda App Secret nem token permanente, então eles não podem vazar por um log ou por um repositório da sua empresa.

Reentrega tratada

Evento duplicado é identificado e descartado, o que evita o cliente receber a mesma resposta automática duas vezes.

Sua aplicação só recebe o que interessa

Se você precisa ligar o WhatsApp ao seu sistema, consome a API da Atys em vez de manter a superfície pública exposta à internet.

Para o contexto mais amplo de como a Meta trata segurança e limites de volume na API, vale ler o guia sobre segurança e escalabilidade na API oficial do WhatsApp.

Resumindo

Validar a assinatura é a camada mais barata de segurança que existe em uma integração com a Cloud API. Ela custa poucas linhas e protege tudo o que vem depois: chatbot, fila, CRM e relatório.

Se você prefere não manter essa superfície exposta e nem os segredos que ela exige, fale com o time da Atys e conecte a sua operação sem abrir uma porta pública no seu servidor.

A validação da assinatura é obrigatória?
A Meta não impede o funcionamento sem ela, mas recomenda em toda a documentação. Na prática, um endpoint que aceita qualquer POST é um endpoint em que qualquer pessoa pode injetar mensagens falsas, então trate como obrigatória.
Minha assinatura nunca bate. O que checar primeiro?
Quase sempre é o corpo da requisição. Confirme que você está usando o payload bruto, e não o array já interpretado pelo framework. Depois confira se o App Secret é do mesmo aplicativo que está enviando os eventos.
Qual a diferença entre o verify token e o App Secret?
O verify token é uma senha que você inventa e que serve apenas no handshake inicial, aquele GET único que cadastra o endpoint. O App Secret é gerado pela Meta e serve para assinar cada evento POST dali em diante.
Preciso validar a assinatura também na rota GET de verificação?
Não. Aquela chamada não tem corpo nem assinatura. Nela você compara o hub.verify_token recebido com o que cadastrou e devolve o hub.challenge. A assinatura vale para os POST de evento.
O que fazer se o App Secret vazar?
Gere um novo imediatamente no Meta for Developers e atualize o ambiente. Enquanto o segredo antigo estiver válido, qualquer pessoa que o tenha consegue assinar payloads que o seu servidor vai aceitar como legítimos.
#Meta #Segurança #webhook #WhatsApp Cloud API #backend
C
Carlos Conteúdo · Atys

Continue lendo

Outros artigos da mesma categoria que podem te ajudar.

O que é WhatsApp Business API? Guia para PMEs em 2026
WhatsApp · 6 min

O que é WhatsApp Business API? Guia para PMEs em 2026

Vários vendedores no mesmo número, celular que desconecta, medo de banimento. Se o WhatsApp da sua empresa já chegou nes...

M Marcelo
12 jun
Como atender vários clientes no mesmo WhatsApp
WhatsApp · 6 min

Como atender vários clientes no mesmo WhatsApp

Vários atendentes no mesmo número do WhatsApp, sem celular circulando pela empresa: as três opções disponíveis, o que mu...

M Marcelo
12 jun
Como dividir o atendimento do WhatsApp entre a equipe
WhatsApp · 8 min

Como dividir o atendimento do WhatsApp entre a equipe

Quando o time cresce, o WhatsApp vira disputa por lead e mensagem esquecida. Veja os três modelos de divisão de atendime...

M Marcelo
12 jun
Dê o próximo passo

Tudo o que sua empresa faz para vender, em um só sistema

Atendimento, CRM, marketing, financeiro e IA na mesma plataforma. Veja funcionando na sua operação.

Agendar demonstração

Recebemos seu contato!

Um especialista da Atys vai falar com você em breve para agendar a demonstração.

Agendar demonstração

Em 20 minutos um especialista mostra a Atys na sua realidade.

BR +55