Webhooks do Discord permitem enviar mensagens automaticamente para canais. Mas sem verificação, qualquer pessoa que saiba sua URL pode enviar mensagens falsas. Isso pode levar a spam, phishing ou vazamento de dados. O Discord não assina requisições de webhook por padrão. Este artigo explica como adicionar uma assinatura às suas requisições de webhook para que seu servidor possa verificar que cada mensagem veio de você.
Principais Conclusões: Protegendo Webhooks do Discord com Requisições Assinadas
- Assinatura HMAC-SHA256: Criptografa o corpo da requisição com uma chave secreta para provar autenticidade.
- Cabeçalho X-Signature-Ed25519: Método alternativo usando criptografia de chave pública Ed25519 para interações assinadas.
- Endpoint de verificação: Seu servidor verifica a assinatura antes de processar qualquer payload do webhook.
Por Que Assinar Requisições de Webhook do Discord é Importante
Uma URL de webhook do Discord é um endpoint HTTP simples. Qualquer pessoa que interceptar ou adivinhar essa URL pode enviar uma requisição POST com qualquer conteúdo. Seu bot ou aplicativo tratará como legítimo. Isso é uma falha de segurança. Assinar a requisição significa que seu remetente adiciona um hash criptográfico aos cabeçalhos HTTP. Seu receptor recalcula o hash usando uma chave secreta compartilhada. Se os hashes coincidirem, a requisição é autêntica. Se não coincidirem, a requisição é rejeitada.
Como Funciona a Verificação de Assinatura
O remetente calcula um hash HMAC-SHA256 do corpo da requisição usando uma chave secreta. O hash é colocado em um cabeçalho personalizado, geralmente X-Signature-256. O receptor lê o cabeçalho, calcula seu próprio HMAC-SHA256 do corpo com a mesma chave secreta e compara os dois. Uma comparação em tempo constante evita ataques de temporização. Este método funciona em qualquer linguagem de programação que suporte HMAC.
Alternativa: Assinaturas Ed25519
O próprio Discord usa assinaturas Ed25519 para seu Interactions Endpoint. Este é um sistema de chave pública. O remetente assina com uma chave privada. O receptor verifica com a chave pública correspondente. Para webhooks personalizados, você pode adotar o mesmo padrão. É mais seguro que HMAC porque a chave privada nunca sai do remetente. No entanto, requer mais configuração e uma biblioteca como nacl ou tweetnacl.
Passos para Assinar e Verificar Requisições de Webhook do Discord
Estes passos assumem que você controla tanto o remetente (seu aplicativo) quanto o receptor (outro servidor ou bot). Você precisa de uma chave secreta compartilhada. Gere uma string hexadecimal aleatória de 32 bytes. Mantenha-a em uma variável de ambiente segura em ambos os lados.
Método 1: Assinatura HMAC-SHA256 (Recomendado para a Maioria dos Usuários)
- Gere uma chave secreta compartilhada
Executeopenssl rand -hex 32no seu terminal. Copie a saída. Esta é sua chave de assinatura. - Crie a assinatura no lado do remetente
Pegue o corpo bruto da requisição como string. CalculeHMAC-SHA256(chave, corpo). Converta o resultado para uma string hexadecimal. Adicione como cabeçalho:X-Signature-256: <string hex>. - Envie a requisição do webhook
Inclua o cabeçalhoX-Signature-256junto com a requisição POST para seu endpoint receptor. - Verifique no lado do receptor
Leia o cabeçalhoX-Signature-256da requisição recebida. CalculeHMAC-SHA256(chave, corpo)usando a mesma chave secreta. Compare as duas strings hexadecimais usando uma função de comparação em tempo constante. Se coincidirem, processe a requisição. Se não, retorne uma resposta 401 Unauthorized.
Método 2: Assinatura Ed25519 (Avançado)
- Gere um par de chaves Ed25519
Use uma biblioteca comotweetnaclem Node.js ouPyNaClem Python. Gere uma chave privada e uma chave pública. Armazene a chave privada no remetente. Compartilhe a chave pública com o receptor. - Assine o corpo da requisição no remetente
Assine a string do corpo bruto com a chave privada. Codifique a assinatura como string hexadecimal. Adicione como cabeçalho:X-Signature-Ed25519: <string hex>. - Envie a requisição
Inclua o cabeçalhoX-Signature-Ed25519com a requisição POST. - Verifique no receptor
Leia o cabeçalho. Use a chave pública para verificar a assinatura contra o corpo. Se a verificação for bem-sucedida, processe a requisição. Se falhar, rejeite-a.
Erros Comuns e Limitações
Incompatibilidade no Nome do Cabeçalho de Assinatura
Ambos os lados devem concordar com o nome exato do cabeçalho. Se o remetente usar X-Signature-256 mas o receptor esperar X-Signature, a verificação falha. Defina o nome do cabeçalho em sua documentação e aplique-o no código.
Diferenças na Codificação do Corpo
A assinatura é calculada a partir da sequência exata de bytes do corpo da requisição. Se o remetente incluir uma quebra de linha no final e o receptor não, os hashes serão diferentes. Use uma codificação consistente, como UTF-8 sem BOM, e evite alterações de espaços em branco.
Ataques de Temporização na Comparação
Nunca use um operador de igualdade simples para comparar strings HMAC. Isso permite que atacantes adivinhem a assinatura byte a byte. Use uma função de comparação em tempo constante fornecida pela sua linguagem. Em Python, use hmac.compare_digest. Em Node.js, use crypto.timingSafeEqual.
Exposição da Chave Secreta
Se sua chave secreta compartilhada vazar, qualquer pessoa pode forjar assinaturas. Rotacione a chave periodicamente. Armazene-a em um gerenciador de segredos ou variável de ambiente. Nunca a codifique diretamente no código-fonte.
HMAC-SHA256 vs Ed25519 para Assinatura de Webhook
| Item | HMAC-SHA256 | Ed25519 |
|---|---|---|
| Tipo de chave | Simétrica (segredo compartilhado) | Assimétrica (privada + pública) |
| Complexidade de configuração | Baixa | Média |
| Força de segurança | Forte | Mais forte (sem segredo compartilhado no receptor) |
| Rotação de chave | Requer atualizar ambos os lados | Apenas rotacionar chave privada |
| Suporte de bibliotecas | Integrado na maioria das linguagens | Requer biblioteca externa |
Assinar suas requisições de webhook do Discord impede que remetentes não autorizados enviem mensagens falsas. Escolha HMAC-SHA256 pela simplicidade ou Ed25519 por segurança mais forte. Implemente comparação em tempo constante e rotacione sua chave secreta regularmente. Após assinar, teste a configuração enviando uma requisição com assinatura errada para confirmar que seu receptor a rejeita.