POST fingindo ser a Ephra — e, se o seu sistema liberar o pedido só porque chegou um JSON com "status": "paid", essa pessoa acabou de comprar de graça.
Esta página descreve o que a Ephra envia hoje, o que ela não envia, e como fechar a porta com o que existe.
O que chega no seu endpoint
Uma entrega real da Ephra é umPOST com estes headers:
User-Agent: Ephra Finance identifica o remetente, mas é um header comum — qualquer cliente HTTP consegue forjá-lo. Ele serve para filtrar ruído em log, não para autenticar.
O signatureSecret não é um segredo protegido
Além de não assinar nada, ele é legível por qualquer chave de API da sua empresa. GET /v1/webhook, a rota transacional legada, devolve o valor inteiro em texto puro a cada listagem — dos mesmos webhooks que GET /v1/webhooks mostra mascarados. As duas rotas leem as mesmas linhas; só o contrato de resposta muda.
totalCount, e o […] esconde o miolo do segredo, que tem 128 caracteres. O signatureSecretMasked: "••••••••146b" que /v1/webhooks devolve para esse mesmo webhook são os 4 últimos caracteres desse valor.
Duas conclusões para o seu desenho de segurança:
- A máscara é conveniência de tela, não proteção. Ela evita que o segredo apareça inteiro num print do painel; ela não impede ninguém de lê-lo.
- Não use o
signatureSecretcomo senha compartilhada — nem como token no caminho da URL, nem num header que o seu endpoint confira. Quem tem uma chave de API da sua empresa já o conhece. Gere o seu próprio segredo para a URL, como no passo a seguir, e guarde-o só do seu lado.
Como proteger o endpoint agora
Três camadas, em ordem de esforço.1
Use uma URL que ninguém adivinha
Cadastre o webhook num caminho com um segredo longo e aleatório dentro:Rejeite com
404 qualquer requisição em outro caminho. É o mesmo princípio de um link de convite: o segredo está na URL. Trate essa URL como senha — fora do log de acesso, fora do controle de versão, fora do chat da equipe.2
Restrinja a origem, se puder
Se o seu ERP fica atrás de um WAF ou de um balanceador, libere o caminho do webhook apenas para as faixas de saída da Ephra e recuse o resto. Peça as faixas atuais a suporte@ephra.io.
3
Confirme pela API antes de liberar
Esta é a camada que realmente decide. Não confie no corpo do Libere o pedido só quando a própria API disser
POST: use-o apenas como gatilho e pergunte à API qual é o estado de verdade."status": "paid", e só quando o valor bater com o que você cobrou. Um POST forjado não sobrevive a essa conferência, porque o atacante não tem a sua chave de API.Quando a assinatura HMAC entrar no ar, esta página passa a descrever o header e o algoritmo, e a conferência pela API continua sendo a recomendação para valores altos. Nenhuma das três camadas acima deixa de valer.
Idempotência
A mesma entrega pode chegar mais de uma vez: uma retentativa depois de um timeout do seu lado, um reenvio manual emPOST /v1/webhook-deliveries/{deliveryId}/retry, ou uma resposta sua que a Ephra não conseguiu ler.
Cada evento traz um id único (um UUID). Guarde-o e descarte duplicatas antes de processar. Sem isso, um cliente recebe dois acessos, ou o seu financeiro contabiliza a mesma venda duas vezes.
Retentativas
Se o seu endpoint não responder2xx, a Ephra reenvia automaticamente.
Uma entrega criada e nunca enviada (órfã) é recuperada depois de 2 minutos pelo mesmo mecanismo — nenhuma fica para trás dentro da janela.
Acompanhe o que falhou em
GET /v1/webhook-deliveries: o campo retries diz em que tentativa a entrega está, e status: "error" marca as que ainda vão voltar.
Checklist
- HTTPS sempre — a Ephra testa a URL no cadastro, mas não impede HTTP.
- Segredo longo e aleatório no caminho da URL;
404em qualquer outro. - Descartar
scope: "test"antes de liberar qualquer coisa. - Idempotência pelo
iddo evento, gravado na mesma transação de banco. - Confirmar
statuse valor emGET /v1/transactions/{transactionId}antes de liberar. - Responder
2xxrápido; processar em segundo plano. - Não validar o payload com schema rígido — campos novos não devem derrubar o seu endpoint.
- Não construir validação de assinatura enquanto não houver header para validar.
- Não usar o
signatureSecretda Ephra como segredo do seu endpoint — ele sai em texto puro emGET /v1/webhook.
