Como verificar assinaturas de webhook do Zendesk: Um guia completo para desenvolvedores
Stevia Putri
Última edição March 2, 2026
Quando você cria integrações que recebem webhooks do Zendesk, você abre um endpoint no seu servidor que aceita requisições HTTP da internet. Sem a verificação adequada, qualquer pessoa poderia enviar requisições falsas para esse endpoint e potencialmente acionar ações indesejadas no seu sistema. É aí que entra a verificação de assinatura.
A verificação de assinatura de webhook do Zendesk oferece uma maneira de provar criptograficamente que os webhooks recebidos realmente vieram do Zendesk e não foram adulterados em trânsito. Este guia orienta você em tudo que você precisa para implementá-lo corretamente, com exemplos de código funcional em cinco linguagens de programação populares.
Se você está procurando uma orientação mais ampla sobre como configurar webhooks no Zendesk, nosso guia de configuração de webhooks de mensagens do Zendesk cobre o processo de configuração completo.

O que é a verificação de assinatura de webhook e por que ela é importante
A verificação de assinatura de webhook é um mecanismo de segurança que permite confirmar a autenticidade das requisições de webhook recebidas. Quando o Zendesk envia um webhook para seu endpoint, ele inclui uma assinatura criptográfica que somente o Zendesk poderia ter gerado. Seu servidor recalcula essa assinatura usando um segredo compartilhado e compara os resultados. Se eles corresponderem, o webhook é genuíno.
Sem essa verificação, seu endpoint fica vulnerável a vários ataques:
- Spoofing: Qualquer pessoa que descobrir seu URL de webhook pode enviar requisições falsas fingindo ser o Zendesk
- Ataques de replay: Um invasor pode capturar um webhook legítimo e reenviá-lo várias vezes
- Adulteração de payload: Os dados da requisição podem ser modificados em trânsito sem detecção
Para integrações de produção que lidam com dados confidenciais de tickets ou acionam fluxos de trabalho automatizados, a verificação de assinatura não é opcional. É um controle de segurança fundamental que protege tanto seu sistema quanto os dados de seus clientes.
Na eesel AI, lidamos com a segurança de webhook automaticamente quando você conecta sua conta Zendesk. Nossa plataforma verifica as assinaturas de forma transparente para que você possa se concentrar na criação de automações em vez de implementações criptográficas.
Como funcionam as assinaturas de webhook do Zendesk
O Zendesk usa o algoritmo SHA256 HMAC (Hash-based Message Authentication Code) para gerar assinaturas de webhook. O processo combina o segredo de assinatura do seu webhook com o payload da requisição e o timestamp para criar uma assinatura exclusiva para cada requisição.
A fórmula se parece com isto:
base64(HMACSHA256(TIMESTAMP + BODY))
Veja o que acontece quando o Zendesk envia um webhook:
- O Zendesk concatena o timestamp e o corpo da requisição bruto em uma única string
- Ele cria um hash HMAC-SHA256 usando o segredo de assinatura do seu webhook como chave
- O hash é codificado em Base64 para produzir a assinatura final
- O Zendesk envia o webhook com dois cabeçalhos críticos:
X-Zendesk-Webhook-Signature- a assinatura geradaX-Zendesk-Webhook-Signature-Timestamp- o timestamp usado na assinatura
Cada requisição de webhook do Zendesk inclui estes cabeçalhos padrão:
x-zendesk-account-id: 123456
x-zendesk-webhook-id: 01F1KRFQ6BG29CNWFR60NK5FNY
x-zendesk-webhook-invocation-id: 8350205582
x-zendesk-webhook-signature: EiqWE3SXTPQpPulBV6OSuuGziIishZNc1VwNZYqZrHU=
x-zendesk-webhook-signature-timestamp: 2021-03-25T05:09:27Z
No seu servidor, você extrai esses cabeçalhos, recalcula a assinatura usando seu segredo de assinatura armazenado e compara os resultados. Se as assinaturas corresponderem, você pode confiar que o webhook veio do Zendesk e que o payload não foi modificado.
Recuperando seu segredo de assinatura de webhook
Antes de poder verificar as assinaturas, você precisa do segredo de assinatura do seu webhook. Cada webhook no Zendesk tem seu próprio segredo exclusivo que é gerado quando o webhook é criado.
Encontrando seu segredo no Admin Center
- Navegue até o Admin Center do Zendesk (Admin Center > Apps and integrations > Webhooks)
- Selecione o webhook que você deseja verificar
- Na página de detalhes do webhook, procure o campo de segredo de assinatura
- Clique em "Reveal secret" (Revelar segredo) para exibir o valor

Trate este segredo como qualquer outra credencial. Não o coloque no código, não o exponha em aplicações do lado do cliente e restrinja o acesso a ele dentro de sua equipe.
Recuperando via API
Você também pode buscar o segredo de assinatura programaticamente usando a API Show webhook signing secret:
GET /api/v2/webhooks/{webhook_id}/signing_secret
Segredo de teste estático para desenvolvimento
Quando você estiver testando webhooks antes de criá-los no Zendesk, você precisará de um segredo de assinatura estático, pois os segredos reais só são gerados após a criação do webhook. Use este segredo de teste durante o desenvolvimento:
dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==
Depois que seu webhook for criado, mude para o segredo de assinatura real. Webhooks de teste e webhooks ativos usam segredos diferentes, então seu código de verificação precisa lidar com o segredo correto para cada ambiente.

Guia de implementação passo a passo
A implementação da verificação de assinatura envolve quatro etapas principais. Vamos detalhar cada uma delas.
Passo 1: Capture o corpo da requisição bruto
A assinatura é calculada no corpo da requisição bruto como uma string, não em JSON analisado ou dados de formulário. Se sua estrutura analisa o corpo antes que você possa acessá-lo, a verificação da assinatura falhará porque os bytes brutos foram transformados.
A maioria das estruturas da web fornece middleware ou opções de configuração para capturar o corpo bruto antes da análise. Normalmente, você precisa armazenar o corpo bruto em uma propriedade como req.rawBody para que ele esteja disponível para o cálculo da assinatura.
Armadilha comum: O middleware de análise de corpo (como express.json() do Express) geralmente é executado antes do seu manipulador de rota. Se o corpo for analisado em um objeto JavaScript antes de você capturar a string bruta, você não poderá recuperar os bytes originais para a verificação da assinatura. Configure seu middleware para capturar o corpo bruto primeiro.
Passo 2: Extraia os cabeçalhos de assinatura
Extraia os dois cabeçalhos relacionados à assinatura da requisição recebida:
X-Zendesk-Webhook-Signature- a assinatura para verificarX-Zendesk-Webhook-Signature-Timestamp- o timestamp usado no cálculo da assinatura
Observe que algumas estruturas transformam os nomes dos cabeçalhos. No Ruby on Rails, por exemplo, o cabeçalho X-Zendesk-Webhook-Signature se torna HTTP_X_ZENDESK_WEBHOOK_SIGNATURE no ambiente de requisição.
Passo 3: Calcule a assinatura esperada
Concatene o timestamp e o corpo bruto e, em seguida, crie um hash HMAC-SHA256 usando seu segredo de assinatura:
- Crie uma string:
timestamp + body(timestamp primeiro, depois o corpo bruto) - Gere HMAC-SHA256 usando seu segredo de assinatura como chave
- Codifique o hash resultante em Base64
Esta assinatura calculada deve corresponder àquela que o Zendesk enviou no cabeçalho X-Zendesk-Webhook-Signature.
Passo 4: Compare as assinaturas com segurança
Use uma função de comparação de tempo constante para comparar as assinaturas. A comparação de string regular (== ou ===) pode vazar informações sobre a assinatura por meio da análise de tempo, o que poderia teoricamente ajudar um invasor a forjar assinaturas válidas.
A maioria das linguagens fornece uma função de comparação de tempo constante:
- Node.js:
crypto.timingSafeEqual() - Python:
hmac.compare_digest() - PHP:
hash_equals() - Ruby:
ActiveSupport::SecurityUtils.secure_compare() - C#: Nenhuma comparação de tempo constante integrada, mas
CryptographicOperations.FixedTimeEquals()no .NET Core
Se as assinaturas corresponderem, processe o webhook. Se elas não corresponderem, retorne uma resposta 401 Unauthorized e registre a falha para investigação.
Exemplos de código em linguagens populares
Aqui estão implementações completas e funcionais para as linguagens de desenvolvimento web mais comuns.
Node.js/Express
const express = require('express');
const crypto = require('crypto');
const SIGNING_SECRET = 'your_webhook_signing_secret_here';
const PORT = 3000;
const app = express();
// Middleware para capturar o corpo bruto
function storeRawBody(req, res, buf) {
if (buf && buf.length) {
req.rawBody = buf.toString('utf8');
}
}
app.use(express.json({ verify: storeRawBody }));
app.use(express.urlencoded({ verify: storeRawBody, extended: true }));
function isValidSignature(signature, body, timestamp) {
const hmac = crypto.createHmac('sha256', SIGNING_SECRET);
const sig = hmac.update(timestamp + body).digest('base64');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(sig)
);
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-zendesk-webhook-signature'];
const timestamp = req.headers['x-zendesk-webhook-signature-timestamp'];
const body = req.rawBody;
if (!isValidSignature(signature, body, timestamp)) {
console.log('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
// Processar o webhook verificado
console.log('Webhook verified, processing...');
res.status(200).send('OK');
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Python (Flask)
from flask import Flask, request, abort
import hmac
import hashlib
import base64
app = Flask(__name__)
SIGNING_SECRET = b'your_webhook_signing_secret_here'
def handle_webhook():
# Obter o corpo bruto
raw_body = request.get_data()
# Extrair cabeçalhos
signature = request.headers.get('X-Zendesk-Webhook-Signature', '')
timestamp = request.headers.get('X-Zendesk-Webhook-Signature-Timestamp', '')
# Calcular a assinatura esperada
signed_payload = (timestamp + raw_body.decode('utf-8')).encode('utf-8')
expected_signature = base64.b64encode(
hmac.new(SIGNING_SECRET, signed_payload, hashlib.sha256).digest()
).decode('utf-8')
# Verificar a assinatura
if not hmac.compare_digest(expected_signature, signature):
abort(401)
# Processar o webhook verificado
return '', 200
if __name__ == '__main__':
app.run(port=3000)
PHP
define('SIGNING_SECRET', 'your_webhook_signing_secret_here');
function verify_webhook($body, $signature, $timestamp) {
// Concatenar timestamp e corpo
$signed_payload = $timestamp . $body;
// Calcular HMAC (saída binária)
$calculated_hmac = base64_encode(
hash_hmac('sha256', $signed_payload, SIGNING_SECRET, true)
);
// Comparação de tempo constante
return hash_equals($signature, $calculated_hmac);
}
// Lidar com a requisição de webhook
$signature = $_SERVER['HTTP_X_ZENDESK_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_ZENDESK_WEBHOOK_SIGNATURE_TIMESTAMP'] ?? '';
$body = file_get_contents('php://input');
if (!verify_webhook($body, $signature, $timestamp)) {
http_response_code(401);
exit('Unauthorized');
}
// Processar o webhook verificado
http_response_code(200);
echo 'OK';
Ruby on Rails
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
SIGNING_SECRET = ENV['ZENDESK_WEBHOOK_SECRET']
def zendesk
signature = request.headers['HTTP_X_ZENDESK_WEBHOOK_SIGNATURE']
timestamp = request.headers['HTTP_X_ZENDESK_WEBHOOK_SIGNATURE_TIMESTAMP']
body = request.body.read
# Calcular a assinatura
signed_payload = timestamp + body
expected_signature = Base64.strict_encode64(
OpenSSL::HMAC.digest('SHA256', SIGNING_SECRET, signed_payload)
)
# Verificar
unless ActiveSupport::SecurityUtils.secure_compare(expected_signature, signature)
head :unauthorized
return
end
# Processar webhook
head :ok
end
end
C#
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;
[]
[]
public class WebhookController : ControllerBase
{
private const string SigningSecret = "your_webhook_signing_secret_here";
[]
public IActionResult HandleWebhook()
{
string signature = Request.Headers["X-Zendesk-Webhook-Signature"];
string timestamp = Request.Headers["X-Zendesk-Webhook-Signature-Timestamp"];
// Ler o corpo bruto
using var reader = new StreamReader(Request.Body);
string body = reader.ReadToEnd();
// Calcular a assinatura
string signedPayload = timestamp + body;
byte[] keyBytes = Encoding.UTF8.GetBytes(SigningSecret);
byte[] payloadBytes = Encoding.UTF8.GetBytes(signedPayload);
using var hmac = new HMACSHA256(keyBytes);
byte[] hash = hmac.ComputeHash(payloadBytes);
string expectedSignature = Convert.ToBase64String(hash);
// Comparar (insensível a maiúsculas e minúsculas para compatibilidade)
if (!signature.Equals(expectedSignature, StringComparison.OrdinalIgnoreCase))
{
return Unauthorized();
}
return Ok();
}
}
Problemas comuns e solução de problemas
Mesmo com o código certo, a verificação da assinatura pode falhar por motivos sutis. Aqui estão os problemas mais comuns que os desenvolvedores encontram.
Espaçamento e formatação JSON
Um dos problemas mais frustrantes envolve a formatação JSON. A assinatura é calculada nos bytes exatos que o Zendesk envia, incluindo espaços em branco. Se sua estrutura reformatar o JSON (adicionando ou removendo espaços), a assinatura não corresponderá.
Um desenvolvedor na comunidade Zendesk descobriu isso da maneira mais difícil:
Descobri. Tinha a ver com o espaçamento na requisição json. O payload json deve ter um espaço antes e depois de ':' e nenhum outro espaço ou tabulação em qualquer outro lugar.
A solução é sempre verificar a assinatura em relação ao corpo da requisição bruto antes que qualquer análise ou transformação ocorra.
Diferenças entre webhook de teste e webhook ativo
Outro problema comum envolve diferenças entre o recurso de webhook de teste do Zendesk e as invocações de webhook ao vivo. O formato do payload pode variar ligeiramente entre os dois, fazendo com que as assinaturas sejam validadas em testes, mas falhem em produção.
Percebi que quando o webhook é executado normalmente, a autenticação da mensagem falha. Então, copiei o corpo da tentativa fracassada, voltei para 'Test Webhook' (Testar Webhook), colei o conteúdo no corpo da mensagem, enviei e a validação da mensagem agora funciona novamente.
Sempre teste com invocações de webhook reais de eventos reais do Zendesk antes de implantar em produção.
Codificação de caracteres
Certifique-se de que o timestamp e o corpo sejam tratados como strings UTF-8 ao concatenar para o cálculo da assinatura. Incompatibilidades de codificação entre seu servidor e o payload do Zendesk causarão falhas de verificação.
Validação de timestamp
Considere adicionar a validação de timestamp para evitar ataques de replay. Verifique se o timestamp no cabeçalho está dentro de uma janela razoável (por exemplo, 5 minutos) da hora atual. Timestamps antigos podem indicar um ataque de replay.
Quando regenerar segredos
Se você suspeitar que seu segredo de assinatura foi comprometido, regenere-o imediatamente através do Admin Center do Zendesk. Após a regeneração, atualize seu servidor com o novo segredo. Pode haver uma breve janela onde os webhooks em trânsito usam o segredo antigo, então considere suportar ambos durante a transição.
Testando sua verificação de webhook
Antes de implantar em produção, teste completamente sua implementação de verificação de assinatura.
Usando o segredo de teste estático
Durante o desenvolvimento, use o segredo de teste estático do Zendesk (dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==) para verificar se sua lógica de implementação está correta. Isso permite que você teste sem criar um webhook ativo.
Testando com o recurso de teste do Zendesk
Quando você cria ou edita um webhook no Zendesk, use o botão "Test Webhook" (Testar Webhook) para enviar payloads de teste. Verifique se seu endpoint aceita essas requisições e valida a assinatura corretamente.
Teste ao vivo
Crie um gatilho real que invoque seu webhook em um evento específico (como a criação de um ticket). Execute essa ação no Zendesk e confirme se seu endpoint recebe e verifica o webhook. Verifique os logs do seu servidor para quaisquer incompatibilidades de assinatura.
Registro e depuração
Registre as seguintes informações durante o desenvolvimento para ajudar a depurar falhas:
- Corpo da requisição bruto (antes da análise)
- Cabeçalho de assinatura recebido
- Assinatura calculada
- Cabeçalho de timestamp
Nunca registre o segredo de assinatura em si. Compare as assinaturas recebidas e calculadas caractere por caractere para identificar onde elas divergem.
Proteja suas integrações Zendesk com eesel AI
Implementar a verificação de assinatura de webhook corretamente requer atenção cuidadosa aos detalhes criptográficos, ao tratamento de corpo específico da estrutura e aos casos extremos em torno da formatação JSON. Para equipes que criam integrações complexas, essa complexidade pode retardar o desenvolvimento e introduzir riscos de segurança.
Na eesel AI, construímos a segurança de webhook diretamente em nossa integração Zendesk. Quando você conecta sua conta Zendesk à nossa plataforma, lidamos com a verificação de assinatura automaticamente. Você obtém os benefícios de segurança sem escrever e manter o código de verificação.

Nosso AI Agent para Zendesk vai além, fornecendo resolução autônoma de tickets enquanto lida com toda a segurança de webhook nos bastidores. Se você está criando automações baseadas em webhook e deseja se concentrar na lógica de negócios em vez de implementações criptográficas, podemos ajudar a simplificar sua integração.
Perguntas Frequentes
Share this article

Article by
Stevia Putri
Stevia Putri is a marketing generalist at eesel AI, where she helps turn powerful AI tools into stories that resonate. She’s driven by curiosity, clarity, and the human side of technology.