A importância de verificar as assinaturas de webhooks
Marcelo Oliveira
29 de junho de 2023
0 minutos de leituraWebhooks são uma técnica de integração por callback usada para enviar e receber informações, como notificações de eventos, quase em tempo real. Eles podem ser acionados por eventos de um aplicativo e transmitir dados por HTTP para outro aplicativo ou API de terceiros. Você pode configurar uma URL de webhook e conectar participantes externos para personalizar, ampliar ou modificar fluxos de trabalho.
Os webhooks podem ou não ser assinados. No entanto, por segurança, é recomendável incluir uma assinatura verificável para que o sistema que recebe a solicitação confirme se ela veio da origem esperada do webhook.
Veja alguns exemplos de como os webhooks são usados atualmente:
Notificações em uma plataforma de controle de versão baseada em Git quando um desenvolvedor envia código para o repositório.
Notificações quando um usuário responde a uma conversa em uma plataforma de mensagens.
Notificações de um serviço de pagamento que confirma a autorização de uma forma de pagamento para uma compra no varejo.
De acordo com um relatório da Verizon, os ataques à cadeia de suprimentos foram responsáveis por 62% de todos os incidentes de invasão de sistemas em 2022. Durante todo o ciclo de vida do desenvolvimento de software, é fundamental manter a segurança de todos os componentes e práticas envolvidos no desenvolvimento e na implantação. Se exploradas, as vulnerabilidades podem comprometer os parceiros envolvidos e interromper toda a cadeia de suprimentos de software.
Para lidar com essas ameaças e manter a segurança da cadeia de suprimentos, os fornecedores da cadeia de suprimentos exigem assinaturas de webhook. Por exemplo, com o CircleCI, você pode usar assinaturas de webhook para validar um segredo e garantir que somente o CircleCI — e não um agente mal-intencionado — acione seu webhook. O Travis CI também fornece um cabeçalho HTTP de assinatura no processo de verificação de webhooks.
Neste passo a passo, vamos implementar um webhook do GitHub em Node.js que detecta quando usuários enviam código para um repositório.
Pré-requisitos
Para acompanhar o passo a passo, você precisará:
Baixar e instalar o Node.js e o npm.
Instalar o ngrok, uma ferramenta de proxy reverso que cria um túnel seguro entre uma URL pública e seu aplicativo Node.js local.
Instalar o Postman, uma plataforma que permite criar solicitações HTTP e inspecionar os resultados com facilidade.
Como verificar assinaturas de webhook usando Node.js
Crie uma nova pasta na sua máquina e um arquivo chamado webhook.js com o código a seguir. Lembre-se de substituir o valor da constante repo pelo caminho do repositório local:
Agora, execute a seguinte linha no terminal:
Veja o que queremos fazer com o arquivo webhook.js:
A constante
repoarmazena o caminho da pasta que contém o repositório que queremos monitorar.A função
createServerinicia um servidor HTTP na nossa máquina, na porta 8080. O aplicativo de webhook está em execução e pronto para receber solicitações no endpoint http://localhost:8080.Depois que a solicitação recebida é processada, a constante body armazena a maior parte do conteúdo da solicitação. Uma solicitação ao nosso endpoint significa que estamos recebendo uma notificação do webhook do GitHub na URL de payload local http://localhost:8080, informando que alguém enviou código para o repositório que estamos monitorando.
Queremos garantir especificamente que o envio tenha sido feito para a branch main. Para verificar isso, analisamos os dados JSON da solicitação em um objeto body e usamos a constante
isMainpara indicar se a referência corresponde à branch main.Se a solicitação for aceita, executamos um comando para entrar no diretório do repositório local. Por fim, executamos os comandos
git fetchegit pullpara atualizá-lo.
Simulando uma solicitação mal-intencionada com o Postman
Estamos desenvolvendo o aplicativo Node.js para funcionar com o GitHub. No entanto, antes de usar o GitHub, vamos simular uma solicitação mal-intencionada com o Postman para mostrar como nosso aplicativo é vulnerável. Isso vai ajudar você a entender a importância das assinaturas.
Abra o aplicativo Postman e configure uma nova solicitação HTTP POST para a URL de payload local http://localhost:8080.
Na aba Body, adicione o seguinte código:

Clique em Send. Abra o terminal onde o Node.js está em execução e observe o que aconteceu:

Como você pode ver, o aplicativo Node.js está aceitando uma solicitação de uma origem diferente do GitHub, o que é perigoso. Precisamos implementar assinaturas de webhook no código para verificar se quem fez a solicitação é legítimo.
Configurando o ngrok
Neste cenário, estamos executando o aplicativo Node.js localmente. No entanto, o GitHub não consegue acessar o endpoint http://localhost:8080 na sua máquina para enviar uma solicitação de webhook. Podemos resolver isso publicando o aplicativo Node.js em um servidor público. Mas é mais fácil executar e depurar o Node.js na máquina de desenvolvimento do que implantá-lo em um servidor web ou na nuvem. Por isso, vamos usar o ngrok. O ngrok é uma ferramenta de proxy reverso que cria um túnel seguro entre uma URL pública aleatória e seu endpoint local de webhook.
Abra uma janela do terminal na sua máquina e execute o seguinte comando do ngrok para expor a porta local 8080:
O ngrok vai confirmar que expôs a porta local 8080 por meio de um túnel seguro com uma URL pública aleatória:
Em seguida, vamos usar o subdomínio público do ngrok acima para configurar um webhook do GitHub.
Configurando um webhook do GitHub
Acesse a página do GitHub para criar um novo repositório e adicione um repositório para configurar o webhook:

Clique em Settings na barra superior. Em seguida, selecione a opção Webhooks na lista à esquerda e clique em Add webhook:

Isso abrirá o formulário Add webhook, no qual vamos configurar um webhook para um evento em que um desenvolvedor envia código para o repositório.

Agora, preencha o formulário da seguinte forma:
No campo Payload URL, insira o endereço do ngrok relativo ao endpoint do seu aplicativo Node.js local.
No campo Content type, selecione "application/json" para indicar o tipo de conteúdo que o GitHub enviará ao endpoint.
No campo Secret, digite "some-webhook-secret". Lembre-se de que, em produção, você precisará usar senhas ou frases mais fortes para garantir a segurança.
Em Which events would you like to trigger this webhook, selecione Just the push event. Assim, o webhook do GitHub será configurado para a ação de envio ao repositório que você pode acionar para testar a verificação.
Observação: É sempre recomendável habilitar a verificação de SSL (Secure Socket Layer) na configuração do webhook. Com a verificação de SSL ativada, as organizações garantem que os dados transmitidos entre sistemas estejam seguros e não possam ser interceptados por pessoas não autorizadas.
Depois de preencher o formulário, clique em Add webhook.

Protegendo o webhook
Precisamos adicionar o módulo crypto do Node.js para lidar com dados criptografados. Adicione esta linha ao primeiro bloco do arquivo webhook.js:
Em seguida, declare uma constante para armazenar a mesma string secreta que você configurou no webhook do GitHub:
Observação: Nunca é recomendável inserir segredos diretamente no código. Este método serve apenas para fins de demonstração.
No início da função req.on, declaramos uma constante para armazenar o valor da assinatura SHA256 calculada com HMAC. Obtenha a assinatura HMAC-SHA256 executando uma função de hash criptográfica SHA256 sobre a chave secreta compartilhada e o valor enviado pela solicitação do GitHub (representado aqui pelo parâmetro chunk):
Em seguida, declaramos a constante isAllowed dentro do evento req.on para indicar se o cabeçalho x-hub-signature-256 corresponde à assinatura que calculamos. Use a função crypto.timingSafeEqual, que compara duas variáveis sem revelar informações de tempo que poderiam ajudar um invasor a adivinhar um dos valores:
No código acima, se houver correspondência, podemos ter certeza de que a solicitação veio do GitHub, e não de um agente mal-intencionado. Se a solicitação não vier do GitHub, o código retornará uma resposta HTTP 401 Unauthorized.
Veja algumas práticas recomendadas aplicadas neste código, que você deve adotar ao desenvolver seus webhooks:
Ao realizar operações com strings para calcular assinaturas de webhook, sempre trate o payload como UTF-8, pois ele pode conter caracteres Unicode. Além disso, se você tiver expectativas sobre como os dados devem aparecer, poderá implementar um método de validação para garantir que os dados recebidos estejam no formato esperado.
Alguns provedores de webhook, como o GitHub, podem incluir um cabeçalho
x-hub-signaturenas solicitações. O cabeçalhox-hub-signatureé gerado com SHA-1, considerado um algoritmo de hash fraco, mas é incluído apenas para garantir compatibilidade com versões anteriores. Sempre que possível, use o cabeçalhox-hub-signature-256, que é gerado com o algoritmo forte SHA-256.Comece a assinatura de hash com
"sha256=", usando o segredo do corpo do payload da solicitação.Em vez de usar um operador de igualdade simples (
"=="ou"==="), use um método de comparação seguro que reduza as chances de ataques de temporização. Todas as linguagens e plataformas modernas têm pelo menos um desses métodos seguros. Neste projeto Node.js, estamos usando a funçãocrypto.timingSafeEqual.
Você pode acessar o código completo de webhook.js aqui.
Para obter mais informações, consulte a documentação do GitHub sobre como proteger webhooks.
Testando o endpoint de webhook protegido
Como você modificou o código, reinicie o servidor Node local. No console, pare o servidor Node com Ctrl+C (ou Command+C no Mac) e execute novamente o comando node webhook.js.
Em seguida, volte ao aplicativo Postman e envie a solicitação novamente. Agora, o aplicativo Node.js responde com o código HTTP 401 Unauthorized:

Abra o GitHub e adicione um arquivo README.md, caso ainda não tenha feito isso, ou abra o arquivo se ele já existir. Em seguida, adicione ou altere o texto conforme abaixo:

Por fim, clique em Commit changes para enviar as alterações para a branch main do repositório:

Agora, volte ao terminal do Node.js para confirmar que o repositório foi buscado e atualizado com sucesso:

Conclusão
Os webhooks permitem a comunicação entre ferramentas e fornecem dados em tempo real quando ocorrem eventos. Você pode integrá-los aos sistemas para criar fluxos de trabalho colaborativos flexíveis e ampliados, algo que não seria possível com APIs que usam parâmetros predefinidos.
Como são abertos por natureza, os webhooks estão sujeitos a ameaças à segurança. Por isso, é essencial tomar as medidas adequadas para prevenir ataques à cadeia de suprimentos.
As assinaturas de webhook são a solução recomendada, pois os provedores de webhook podem configurar segredos e gerar assinaturas usando algoritmos de hash fortes. As assinaturas são uma forma simples e segura de garantir que a comunicação de dados entre webhooks aconteça somente entre parceiros verificados e confiáveis.
Comece a jogar Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.



