# Carga de webhook cifrada ponta a ponta: o que a assinatura não resolve

<https://unifokal.com/blog/carga-de-webhook-cifrada-ponta-a-ponta>

Guia · UNIFOKAL · 23 de setembro de 2026 · 6 min de leitura · Produto e integração

Assinar o webhook prova quem mandou e que ninguém mexeu. Não esconde o conteúdo de quem está no caminho. Como a cifra da carga com chave do cliente fecha essa última brecha.

Quase toda integração por webhook no mercado resolve duas perguntas e para ali. A primeira é de autoria: foi mesmo o fornecedor que mandou isto? A segunda é de integridade: o corpo chegou como saiu? A assinatura HMAC com carimbo de tempo, o padrão que a indústria copiou da documentação pública da Stripe e que a [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104.html) define, responde as duas com folga.

Existe uma terceira pergunta que ela não responde, e é a que aparece na primeira revisão de segurança séria de qualquer integração de KYC: **quem consegue ler o conteúdo no caminho?** Assinar não esconde nada. O corpo assinado é texto legível para qualquer coisa que o toque entre a nossa saída e o seu processo.

## Onde o corpo em claro realmente passa

O reflexo é responder "ninguém, tem TLS". O TLS resolve o trecho de rede, e resolve bem. O problema é que a infraestrutura moderna encerra o TLS bem antes do seu código.

Entre a nossa chamada e a função que valida a assinatura, o corpo costuma passar, já decifrado, por um balanceador gerenciado, por um gateway de API, por uma malha de serviço, por um proxy reverso da própria empresa e, com frequência, por um agregador de log que guarda corpo de requisição para depuração. Cada um desses pontos é um lugar onde o nome completo, o CPF e o resultado da verificação de uma pessoa existem em texto legível, sob a política de retenção de um sistema que ninguém tratou como sistema de dado pessoal.

Some a isso o fato de que muita equipe registra o corpo do webhook no log de aplicação enquanto depura a integração, e depois esquece o registro ligado. O resultado é um segundo banco de dados sensível, não governado, que a Lei 13.709/2018 alcança exatamente como alcança o banco principal. É o erro mais comum das auditorias de integração, e ele já aparece na nossa lista de armadilhas em [como receber webhooks com segurança](https://unifokal.com/blog/webhooks-seguros-em-kyc).

## A ideia: a chave de leitura é do cliente, não nossa

A saída é simples de enunciar e exige disciplina para implementar: o corpo sai cifrado para uma chave pública que o cliente registra, e só a chave privada correspondente abre. Essa chave privada nasce na máquina do cliente, fica na máquina do cliente e nunca é enviada a nós. Não existe campo para ela no painel, na API, no SDK nem em coluna nenhuma do nosso banco.

A consequência prática é boa e vale dizer em voz alta: nenhum intermediário passa a conseguir ler o conteúdo, e nós também não conseguimos ler o que já saiu. O balanceador vê um envelope. O gateway vê um envelope. O agregador de log que estava guardando corpo de requisição passa a guardar um envelope. A superfície de exposição deixa de ser "todo mundo no caminho" e passa a ser "quem tem a chave privada", que é uma lista que o cliente controla.

O mecanismo padronizado para isso é o **HPKE**, definido pela [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180.html) do IETF. Ele combina uma troca de chaves de curva elíptica com uma cifra autenticada, e foi desenhado justamente para o caso de cifrar uma mensagem avulsa para um destinatário conhecido, sem negociação prévia e sem sessão. É o mesmo alicerce que o ECH do TLS e o Message Layer Security usam.

## Três detalhes que separam o desenho certo do desenho ingênuo

**A ordem das camadas não se inverte.** Quem recebe valida a assinatura sobre os bytes crus que chegaram e só depois decifra. Fazer o contrário significa gastar a sua chave privada em conteúdo de origem desconhecida, que é exatamente a superfície que um atacante quer alcançar. A assinatura continua sendo a porta; a cifra é o que está atrás dela.

**O envelope é amarrado ao evento.** Uma cifra bem feita não produz um bloco que possa ser recortado e colado em outra mensagem. O material que deriva a chave de cifra inclui o identificador do evento, então um bloco cifrado transplantado para outro envelope simplesmente não abre. Sem isso, a cifra protegeria o conteúdo e deixaria de proteger o contexto, e quem recebe processaria o dado certo atribuído ao evento errado.

**A troca de chave não pode derrubar a entrega.** Vale aqui o mesmo princípio de [rotação de chave e segredo de webhook](https://unifokal.com/blog/rotacao-de-chave-e-segredo-de-webhook): a chave nova nasce ao lado da antiga e a antiga é aposentada, não apagada. Uma entrega que já saiu cifrada para a chave anterior continua sendo aberta por ela em uma retentativa ou em um reenvio manual, porque reenviar precisa mandar os mesmos bytes, e não montar um corpo novo. Por isso o envelope carrega, em claro, qual chave o abre: é um identificador, não um segredo.

## O que a cifra não resolve, e é honesto dizer

A cifra da carga não substitui a assinatura, não substitui a janela anti repetição e não substitui a reconciliação. São controles diferentes para perguntas diferentes.

A assinatura continua sendo a única coisa que prova autoria. A [janela anti repetição](https://unifokal.com/blog/janela-anti-replay-em-webhook) continua sendo o que impede que uma entrega legítima capturada seja reapresentada depois. E a [reconciliação](https://unifokal.com/blog/reconciliacao-de-webhook) continua sendo o que fecha o buraco de um endpoint que ficou fora do ar por mais tempo que a janela de retentativa, porque webhook é notificação e a fonte da verdade é consultável.

Também vale registrar o limite: o cabeçalho que diz qual é o tipo do evento continua em claro, porque é ele que permite rotear a entrega sem abrir o corpo. Quem estiver no caminho continua vendo que houve um evento e de que tipo ele é. O que deixa de ver é de quem, com qual documento e com qual resultado.

## Quando ligar

A cifra da carga é opcional por destino, e essa escolha é de desenho. Ligar tudo por padrão obrigaria toda integração existente a mudar de código no mesmo dia, o que é o oposto de segurança operacional. Então a régua prática é: ligue nos destinos que trafegam resultado de verificação de pessoa, especialmente quando o corpo atravessa infraestrutura compartilhada, quando existe agregador de log corporativo no caminho, ou quando a sua própria política interna classifica o dado como restrito e você precisa provar ao auditor onde ele fica legível.

O formato exato do envelope, os passos para registrar a chave e o código pronto nas duas linguagens estão na [referência de webhooks](https://unifokal.com/docs/webhooks). O resto é o de sempre: responda rápido, processe depois, e trate o seu endpoint como parte da sua superfície de segurança, não como um detalhe de integração.

## Fontes citadas

- R. Barnes, K. Bhargavan, B. Lipp e C. Wood. RFC 9180, Hybrid Public Key Encryption. IETF, fevereiro de 2022: [rfc-editor.org/rfc/rfc9180.html](https://www.rfc-editor.org/rfc/rfc9180.html)
- H. Krawczyk, M. Bellare e R. Canetti. RFC 2104, HMAC: Keyed-Hashing for Message Authentication. IETF, fevereiro de 1997: [rfc-editor.org/rfc/rfc2104.html](https://www.rfc-editor.org/rfc/rfc2104.html)
- A. Langley, M. Hamburg e S. Turner. RFC 7748, Elliptic Curves for Security. IETF, janeiro de 2016: [rfc-editor.org/rfc/rfc7748.html](https://www.rfc-editor.org/rfc/rfc7748.html)
- Documentação pública de webhooks da Stripe, referência da indústria para assinatura com carimbo de tempo e janela de tolerância: [docs.stripe.com/webhooks](https://docs.stripe.com/webhooks)
- Lei 13.709/2018, LGPD, sobre o alcance do tratamento de dados pessoais em logs e sistemas auxiliares, no portal do Planalto: [planalto.gov.br](https://www.planalto.gov.br/ccivil_03/_ato2015-2018/2018/lei/l13709.htm)
