Guia completo de segurança de webhooks

Um webhook é um mecanismo de comunicação HTTP rápido, simples e eficiente para integrar aplicações remotas. Essa simplicidade e eficiência, embora empolgantes, envolvem um trade-off muito crítico: segurança. Webhooks não foram construídos para ser seguros por padrão, e todo o ônus da segurança recai sobre a pessoa que desenvolve. Um argumento a favor disso é que a quantidade de segurança necessária para um webhook é, em boa medida, relativa ao seu caso de uso.

Enquanto casos como enviar dados de log de aplicação para sistemas de visualização podem não se importar muito com a criptografia dos dados em trânsito, casos envolvendo a comunicação de dados financeiros se importam.

Esse modelo de segurança do tipo "construa quanto você se importar" permite que qualquer aplicação que trabalhe com webhooks (produtores e consumidores) adote os controles de segurança que melhor se ajustem à sensibilidade dos dados comunicados via webhooks.

Neste artigo, vamos analisar as vulnerabilidades de segurança dos webhooks. Em seguida, exploraremos as diferentes estratégias para mitigar essas falhas.

Proteja seus webhooks automaticamente.

O Hookdeck verifica assinaturas de webhooks de mais de 160 provedores — para que você foque no processamento, não na autenticação.

Preocupações de segurança com webhooks

Nesta seção, começamos discutindo as falhas de segurança no design dos webhooks. Isso vai ajudar você a entender melhor os problemas que está tentando resolver e o "porquê" de cada estratégia de segurança que escolher adotar. Cubro aqui os principais pontos a ter em mente, mas para mais informações sobre vulnerabilidades de webhooks você pode continuar lendo aqui.

Não há verificação da origem e do destino de um webhook

O mecanismo de comunicação por webhook não tem uma forma nativa de identificar a origem ou o destino de um webhook. Isso significa que um produtor de webhooks não tem como verificar se está enviando o webhook para o destino correto, e o consumidor não consegue verificar se está recebendo o webhook da origem esperada.

Essa vulnerabilidade permite que qualquer um se passe por produtor ou receptor de webhooks. Agentes maliciosos podem se aproveitar facilmente dessa falha para atuar como produtores e enviar webhooks maliciosos a um consumidor, com o objetivo de comprometer a aplicação receptora.

O erro humano também pode fazer com que desenvolvedores digitem a URL errada ao configurar um webhook. Como o webhook não verifica o destino, as requisições serão disparadas para o endpoint errado, o que causa perda ou comprometimento de dados se o erro não for detectado rapidamente.

Os dados do webhook ficam expostos em trânsito

Webhooks usam o protocolo HTTP por padrão. O protocolo HTTP transmite dados em texto puro, o que significa que eles trafegam abertamente de uma aplicação para outra. Webhooks usam o protocolo como ele é, sem nenhuma camada extra de proteção para a transmissão dos dados.

Essa vulnerabilidade permite que qualquer pessoa com visibilidade sobre o tráfego de rede envolvendo webhooks visualize e extraia informações deles facilmente. As informações comprometidas dessa forma não se limitam ao payload do webhook: headers sensíveis contendo dados de autenticação também podem ser lidos e até adulterados.

Os dados do webhook não são verificados

Imagine que você comprou um microfone de mesa na Amazon. A Amazon cobra o preço certo, acerta o seu endereço e entrega o pacote dentro do prazo previsto. Está tudo bem, certo? O processo de entrega foi tranquilo e bem-sucedido, não foi? Aí você abre o pacote e percebe que ele contém um jogo de xícaras de chá. Mesmo com um processo de entrega sem contratempos, você recebeu o item errado, e isso automaticamente arruína toda a experiência de compra.

Isso é parecido com a falha na verificação dos dados contidos em um webhook. Outro problema importante, diferentemente do exemplo da Amazon em que o cliente sabia o que esperar, é que os consumidores de webhooks não fazem ideia de quais dados esperar e, ainda assim, não têm como verificar se são os dados corretos.

Aceitar dados incorretos no seu sistema por causa de uma falha de verificação pode ter efeitos realmente danosos sobre a integridade da sua aplicação.

Não há restrição de onde um webhook pode vir

Outra vulnerabilidade de segurança no design dos webhooks é que, embora produtores possam direcionar webhooks a consumidores específicos, não há como os consumidores restringirem as origens de onde recebem webhooks.

Isso permite que qualquer aplicação envie webhooks para a URL de webhook de um consumidor, com efeito semelhante ao da falta de verificação nas duas pontas da transação.

Embora o whitelisting de IPs às vezes possa ser implementado para mitigar essa falha, restringindo quais produtores podem enviar webhooks a determinado cliente, a comunicação através de proxies ou servidores de fila não usa o endereço IP do produtor. Além disso, o IP spoofing, processo usado por atacantes para se passar por um host imitando o seu endereço IP, não é tão difícil de conseguir hoje em dia.

Uma requisição de webhook pode ser duplicada

Webhooks costumam ser enviados com dados que podem causar uma mudança no banco de dados da aplicação receptora ou disparar uma ação, como enviar um e-mail. Algumas dessas ações, se repetidas, podem levar a um estado inconsistente na aplicação receptora. Isso pode comprometer a integridade do banco de dados ou fazer com que uma ação se repita de forma incômoda (por exemplo, enviar várias vezes a mesma notificação a um usuário).

Um webhook, do lado do consumidor, não tem como detectar se está sendo repetido. Muitas vezes, os produtores têm alguma forma de receber a informação de que um webhook falhou no receptor, e alguns produtores reenviam o webhook automaticamente nesse caso. No entanto, a mensagem de falha que o consumidor devolve pode ter ocorrido depois que uma mudança no estado da aplicação já foi causada pelo webhook. Assim, um retry automático ou manual da requisição vai duplicar o efeito.

Além disso, atacantes que consigam capturar uma requisição de webhook podem reproduzi-la várias vezes, fazendo com que o consumidor repita a mudança de estado pretendida múltiplas vezes. Por exemplo, uma transação de crédito que adiciona US$ 5 à carteira de um cliente em uma aplicação financeira pode ser reproduzida várias vezes até somar US$ 200.

Como proteger um webhook?

Agora que examinamos as vulnerabilidades do mecanismo de comunicação por webhook, como protegê-lo?

Webhooks deveriam ter autenticação? Como tornar os dados mais seguros para que não sejam comprometidos se interceptados em trânsito? Como restringir as origens dos webhooks?

Um checklist de segurança

Quando se trata de proteger webhooks, existem boas práticas que ajudam a superar boa parte das vulnerabilidades descritas acima.

Felizmente, temos um artigo inteiro dedicado a detalhar essas verificações de segurança necessárias para uma proteção adequada. Ainda assim, vou resumir as estratégias abaixo, e você pode consultar o artigo do checklist para mais detalhes.

  • Verifique a origem e o consumidor: usar um framework de autenticação como a autenticação por token combinado com Mutual TLS permite verificar, respectivamente, a origem e o destino dos webhooks.
  • Criptografe todos os dados: o TLS ajuda a adicionar uma camada extra à pilha TCP/IP que criptografa todos os dados HTTP antes do envio. Por isso, recomenda-se que as URLs de webhook usem o protocolo HTTP seguro, HTTPS. Isso garante que todos os dados em trânsito sejam criptografados e se tornem inúteis para quem os intercepte com fins maliciosos.
  • Verifique a mensagem: a verificação da mensagem pode ser feita por meio de um sistema de verificação de assinatura. Estratégias de autenticação criptográfica como o Hash-based Message Authentication Code (HMAC) são usadas para criar um valor de hash do payload tanto no servidor quanto no cliente. Os valores dos dois lados são então comparados para verificar a integridade dos dados.
  • Use um "nonce" com timestamp para prevenir replay attacks: essa estratégia envolve anexar ao webhook um timestamp de expiração da mensagem antes de calcular a assinatura. Depois que o tempo definido expira, a mensagem deixa de ser válida e quem tentar reproduzir o webhook será rejeitado. O tempo de expiração pode ser definido conforme a sua preferência. Um dos provedores de webhooks que adota essa estratégia é a Stripe.

Com isso no lugar, você previne quase todas as vulnerabilidades discutidas na seção anterior.

Autenticação de webhooks

A autenticação de webhooks ajuda a confirmar que estamos recebendo os webhooks da origem esperada e que eles estão sendo enviados ao destino correto. Por meio da autenticação, também podemos validar a mensagem enviada no webhook para garantir que ela não foi adulterada.

Alguns dos métodos de autenticação de webhooks mais comuns hoje são:

  • Basic authentication
  • Autenticação por token
  • Verificação de assinatura

Como veremos no próximo artigo desta série, "Estratégias de autenticação de webhooks", a autenticação de webhooks resolve boa parte das vulnerabilidades de segurança descritas acima.

Uma forma de checar se o seu provedor exige autenticação, qual é o tipo de autenticação e como ela se comporta é testar um único webhook no seu endpoint em ambiente de desenvolvimento. Montar esse tipo de teste pode ser trabalhoso, já que exige um endpoint HTTPs publicamente acessível. Felizmente, você pode usar a nossa ferramenta online de teste de webhooks para testar webhooks facilmente, roteando-os para o seu ambiente de desenvolvimento.

Exemplo: implementando verificação de assinatura em Node JS

Uma das formas mais fortes e mais usadas de autenticação de webhooks é a verificação de assinatura. Ela é poderosa por ser capaz não só de autenticar o produtor e o consumidor do webhook, mas também de validar o payload.

A verificação de assinatura faz isso levando o provedor e o consumidor do webhook a calcular uma assinatura criptográfica única e, então, comparar as duas. Se houver correspondência, o webhook é válido; se não, é rejeitado.

Se você quiser se aprofundar em como a verificação de assinatura é implementada e ver mais exemplos de código em diferentes linguagens, confira o nosso artigo sobre como implementar verificação de assinatura.

Aqui está uma implementação de verificação de assinatura para um webhook em um servidor Node.js com Express:

const express = require("express");
const routes = require("./routes");
const bodyParser = require("body-parser");
const crypto = require("crypto");

// App
const app = express();

const sigHeaderName = "X-Signature-SHA256";
const sigHashAlg = "sha256";
const sigPrefix = ""; //set this to your signature prefix if any
const secret = "my_webhook_api_secret";

//Get the raw body
app.use(
  bodyParser.json({
    verify: (req, res, buf, encoding) => {
      if (buf && buf.length) {
        req.rawBody = buf.toString(encoding || "utf8");
      }
    },
  }),
);

//Validate payload
function validatePayload(req, res, next) {
  if (req.get(sigHeaderName)) {
    //Extract Signature header
    const sig = Buffer.from(req.get(sigHeaderName) || "", "utf8");

    //Calculate HMAC
    const hmac = crypto.createHmac(sigHashAlg, secret);
    const digest = Buffer.from(
      sigPrefix + hmac.update(req.rawBody).digest("hex"),
      "utf8",
    );

    //Compare HMACs
    if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) {
      return res.status(401).send({
        message: `Request body digest (${digest}) did not match ${sigHeaderName} (${sig})`,
      });
    }
  }

  return next();
}
app.use(validatePayload);

app.use("/", routes);

const port = process.env.PORT || "1337";
app.set("port", port);

app.listen(port, () => console.log(`Server running on localhost:${port}`));

No código acima, o consumidor do webhook calcula a assinatura usando uma chave secreta, o payload do webhook e a função de hash SHA-256. A assinatura calculada é então comparada com a que veio no header de assinatura X-Signature-SHA256 para confirmar a correspondência.

Como o Hookdeck ajuda

Implementar o conjunto completo de controles de segurança de webhooks abordados neste guia (verificação de assinatura, proteção contra replay, gestão de segredos, allowlist de IPs) é simples para um único provedor, mas a complexidade se acumula a cada integração. Um único descompasso significa ou rejeitar webhooks legítimos ou, pior, aceitar webhooks forjados. O custo de manutenção só cresce a cada novo provedor integrado.

O Event Gateway do Hookdeck faz a verificação de assinatura para mais de 160 provedores de webhooks, prontos para uso. Você seleciona o provedor, informa o signing secret, e toda requisição recebida é verificada na borda antes de chegar à sua aplicação; requisições não verificadas são rejeitadas automaticamente. Isso significa que os seus handlers podem confiar em todo payload que chega, sem que você mantenha lógica de verificação específica por provedor nem rotacione segredos espalhados pelo código. Comece a usar o Hookdeck e tire a autenticação de webhooks das suas costas em minutos.

Conclusão

A segurança de webhooks é fácil de negligenciar, e isso pode ter efeitos muito danosos sobre a integridade das aplicações envolvidas. No entanto, seguir boas práticas que amarrem todas as pontas soltas por onde webhooks podem ser atacados ajuda a evitar as consequências de uma brecha no pipeline de dados.

Neste artigo, discutimos as falhas de segurança na arquitetura de um webhook e vimos como essas falhas podem ser eliminadas.

Bom código!