Cómo verificar las firmas de webhook de Zendesk: Una guía completa para desarrolladores

Stevia Putri
Written by

Stevia Putri

Reviewed by

Stanley Nicholas

Last edited 2 marzo 2026

Expert Verified

Imagen del banner para Cómo verificar las firmas de webhook de Zendesk: Una guía completa para desarrolladores

Cuando crea integraciones que reciben webhooks de Zendesk, abre un punto final en su servidor que acepta solicitudes HTTP desde Internet. Sin la verificación adecuada, cualquiera podría enviar solicitudes falsas a ese punto final y potencialmente desencadenar acciones no deseadas en su sistema. Ahí es donde entra en juego la verificación de firmas.

La verificación de firmas de webhook de Zendesk le brinda una forma de probar criptográficamente que los webhooks entrantes realmente provienen de Zendesk y no fueron manipulados en tránsito. Esta guía lo guía a través de todo lo que necesita para implementarlo correctamente, con ejemplos de código funcional en cinco lenguajes de programación populares.

Si está buscando una guía más amplia sobre cómo configurar webhooks en Zendesk, nuestra guía de configuración de webhooks de mensajería de Zendesk cubre el proceso de configuración completo.

Panel de simulación de eesel AI que muestra las tasas de automatización predichas para una integración de Zendesk ChatGPT
Panel de simulación de eesel AI que muestra las tasas de automatización predichas para una integración de Zendesk ChatGPT

Qué es la verificación de firmas de webhook y por qué es importante

La verificación de firmas de webhook es un mecanismo de seguridad que le permite confirmar la autenticidad de las solicitudes de webhook entrantes. Cuando Zendesk envía un webhook a su punto final, incluye una firma criptográfica que solo Zendesk podría haber generado. Su servidor vuelve a calcular esa firma utilizando un secreto compartido y compara los resultados. Si coinciden, el webhook es genuino.

Sin esta verificación, su punto final es vulnerable a varios ataques:

  • Suplantación (Spoofing): Cualquiera que descubra la URL de su webhook podría enviar solicitudes falsas pretendiendo ser Zendesk.
  • Ataques de repetición (Replay attacks): Un atacante podría capturar un webhook legítimo y reenviarlo varias veces.
  • Manipulación de la carga útil (Payload tampering): Los datos de la solicitud podrían modificarse en tránsito sin ser detectados.

Para las integraciones de producción que manejan datos confidenciales de tickets o que desencadenan flujos de trabajo automatizados, la verificación de firmas no es opcional. Es un control de seguridad fundamental que protege tanto su sistema como los datos de sus clientes.

En eesel AI, manejamos la seguridad de los webhooks automáticamente cuando conecta su cuenta de Zendesk. Nuestra plataforma verifica las firmas de forma transparente para que pueda concentrarse en la creación de automatizaciones en lugar de implementaciones criptográficas.

Cómo funcionan las firmas de webhook de Zendesk

Zendesk utiliza el algoritmo SHA256 HMAC (Código de autenticación de mensajes basado en hash) para generar firmas de webhook. El proceso combina el secreto de firma de su webhook con la carga útil de la solicitud y la marca de tiempo para crear una firma única para cada solicitud.

La fórmula se ve así:

base64(HMACSHA256(TIMESTAMP + BODY))

Esto es lo que sucede cuando Zendesk envía un webhook:

  1. Zendesk concatena la marca de tiempo y el cuerpo de la solicitud sin procesar en una sola cadena.
  2. Crea un hash HMAC-SHA256 utilizando el secreto de firma de su webhook como clave.
  3. El hash se codifica en Base64 para producir la firma final.
  4. Zendesk envía el webhook con dos encabezados críticos:
    • X-Zendesk-Webhook-Signature: la firma generada
    • X-Zendesk-Webhook-Signature-Timestamp: la marca de tiempo utilizada en la firma

Cada solicitud de webhook de Zendesk incluye estos encabezados estándar:

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

En su servidor, extrae estos encabezados, vuelve a calcular la firma utilizando su secreto de firma almacenado y compara los resultados. Si las firmas coinciden, puede confiar en que el webhook provino de Zendesk y que la carga útil no ha sido modificada.

Flujo de verificación de firmas de webhook desde Zendesk a su servidor
Flujo de verificación de firmas de webhook desde Zendesk a su servidor

Recuperando su secreto de firma de webhook

Antes de que pueda verificar las firmas, necesita el secreto de firma para su webhook. Cada webhook en Zendesk tiene su propio secreto único que se genera cuando se crea el webhook.

Encontrando su secreto en el Centro de administración

  1. Navegue al Centro de administración de Zendesk (Centro de administración > Aplicaciones e integraciones > Webhooks)
  2. Seleccione el webhook que desea verificar
  3. En la página de detalles del webhook, busque el campo de secreto de firma
  4. Haga clic en "Revelar secreto" para mostrar el valor

Interfaz de configuración de webhook de Zendesk que muestra los métodos de conexión
Interfaz de configuración de webhook de Zendesk que muestra los métodos de conexión

Trate este secreto como cualquier otra credencial. No lo guarde en el código, no lo exponga en aplicaciones del lado del cliente y restrinja el acceso al mismo dentro de su equipo.

Recuperando a través de la API

También puede obtener el secreto de firma mediante programación utilizando la API Mostrar secreto de firma de webhook:

GET /api/v2/webhooks/{webhook_id}/signing_secret

Secreto de prueba estático para el desarrollo

Cuando esté probando webhooks antes de crearlos en Zendesk, necesitará un secreto de firma estático, ya que los secretos reales solo se generan después de la creación del webhook. Utilice este secreto de prueba durante el desarrollo:

dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==

Una vez que se crea su webhook, cambie al secreto de firma real. Los webhooks de prueba y los webhooks en vivo utilizan diferentes secretos, por lo que su código de verificación debe manejar el secreto correcto para cada entorno.

Captura de pantalla de la página de inicio de Zendesk
Captura de pantalla de la página de inicio de Zendesk

Guía de implementación paso a paso

La implementación de la verificación de firmas implica cuatro pasos clave. Analicemos cada uno de ellos.

Paso 1: Capturar el cuerpo de la solicitud sin procesar

La firma se calcula sobre el cuerpo de la solicitud sin procesar como una cadena, no sobre JSON analizado o datos de formulario. Si su marco de trabajo analiza el cuerpo antes de que pueda acceder a él, la verificación de la firma fallará porque los bytes sin procesar se han transformado.

La mayoría de los marcos de trabajo web proporcionan middleware u opciones de configuración para capturar el cuerpo sin procesar antes del análisis. Por lo general, necesita almacenar el cuerpo sin procesar en una propiedad como req.rawBody para que esté disponible para el cálculo de la firma.

Error común: el middleware de análisis del cuerpo (como express.json() de Express) a menudo se ejecuta antes que el controlador de su ruta. Si el cuerpo se analiza en un objeto JavaScript antes de capturar la cadena sin procesar, no puede recuperar los bytes originales para la verificación de la firma. Configure su middleware para capturar el cuerpo sin procesar primero.

Paso 2: Extraer los encabezados de firma

Extraiga los dos encabezados relacionados con la firma de la solicitud entrante:

  • X-Zendesk-Webhook-Signature: la firma para verificar
  • X-Zendesk-Webhook-Signature-Timestamp: la marca de tiempo utilizada en el cálculo de la firma

Tenga en cuenta que algunos marcos de trabajo transforman los nombres de los encabezados. En Ruby on Rails, por ejemplo, el encabezado X-Zendesk-Webhook-Signature se convierte en HTTP_X_ZENDESK_WEBHOOK_SIGNATURE en el entorno de la solicitud.

Paso 3: Calcular la firma esperada

Concatene la marca de tiempo y el cuerpo sin procesar, luego cree un hash HMAC-SHA256 utilizando su secreto de firma:

  1. Cree una cadena: timestamp + body (primero la marca de tiempo, luego el cuerpo sin procesar)
  2. Genere HMAC-SHA256 utilizando su secreto de firma como clave
  3. Codifique el hash resultante en Base64

Esta firma calculada debe coincidir con la que Zendesk envió en el encabezado X-Zendesk-Webhook-Signature.

Paso 4: Comparar firmas de forma segura

Utilice una función de comparación de tiempo constante para comparar las firmas. La comparación de cadenas regular (== o ===) puede filtrar información sobre la firma a través del análisis de tiempo, lo que teóricamente podría ayudar a un atacante a falsificar firmas válidas.

La mayoría de los lenguajes proporcionan una función de comparación de tiempo constante:

  • Node.js: crypto.timingSafeEqual()
  • Python: hmac.compare_digest()
  • PHP: hash_equals()
  • Ruby: ActiveSupport::SecurityUtils.secure_compare()
  • C#: No hay comparación de tiempo constante incorporada, pero CryptographicOperations.FixedTimeEquals() en .NET Core

Si las firmas coinciden, procese el webhook. Si no coinciden, devuelva una respuesta 401 No autorizado y registre el fallo para su investigación.

Ejemplos de código en lenguajes populares

Aquí hay implementaciones completas y funcionales para los lenguajes de desarrollo web más comunes.

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 to capture raw body
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');
  }

  // Process the verified webhook
  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'

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # Get raw body
    raw_body = request.get_data()

    # Extract headers
    signature = request.headers.get('X-Zendesk-Webhook-Signature', '')
    timestamp = request.headers.get('X-Zendesk-Webhook-Signature-Timestamp', '')

    # Calculate expected signature
    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')

    # Verify signature
    if not hmac.compare_digest(expected_signature, signature):
        abort(401)

    # Process verified webhook
    return '', 200

if __name__ == '__main__':
    app.run(port=3000)

PHP

<?php

define('SIGNING_SECRET', 'your_webhook_signing_secret_here');

function verify_webhook($body, $signature, $timestamp) {
    // Concatenate timestamp and body
    $signed_payload = $timestamp . $body;

    // Calculate HMAC (binary output)
    $calculated_hmac = base64_encode(
        hash_hmac('sha256', $signed_payload, SIGNING_SECRET, true)
    );

    // Constant-time comparison
    return hash_equals($signature, $calculated_hmac);
}

// Handle webhook request
$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');
}

// Process verified webhook
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

    # Calculate signature
    signed_payload = timestamp + body
    expected_signature = Base64.strict_encode64(
      OpenSSL::HMAC.digest('SHA256', SIGNING_SECRET, signed_payload)
    )

    # Verify
    unless ActiveSupport::SecurityUtils.secure_compare(expected_signature, signature)
      head :unauthorized
      return
    end

    # Process webhook
    head :ok
  end
end

C#

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
    private const string SigningSecret = "your_webhook_signing_secret_here";

    [HttpPost]
    public IActionResult HandleWebhook()
    {
        string signature = Request.Headers["X-Zendesk-Webhook-Signature"];
        string timestamp = Request.Headers["X-Zendesk-Webhook-Signature-Timestamp"];

        // Read raw body
        using var reader = new StreamReader(Request.Body);
        string body = reader.ReadToEnd();

        // Calculate signature
        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);

        // Compare (case-insensitive for compatibility)
        if (!signature.Equals(expectedSignature, StringComparison.OrdinalIgnoreCase))
        {
            return Unauthorized();
        }

        return Ok();
    }
}

Problemas comunes y resolución de problemas

Incluso con el código correcto, la verificación de la firma puede fallar por razones sutiles. Estos son los problemas más comunes que encuentran los desarrolladores.

Espaciado y formato JSON

Uno de los problemas más frustrantes involucra el formato JSON. La firma se calcula sobre los bytes exactos que Zendesk envía, incluido el espacio en blanco. Si su marco de trabajo reformatea el JSON (agregando o eliminando espacios), la firma no coincidirá.

Un desarrollador en la comunidad de Zendesk descubrió esto por las malas:

Comunidad de Zendesk
Lo resolví. Tenía que ver con el espaciado en la solicitud json. La carga útil json debe tener un espacio antes y después de ':' y ningún otro espacio o tabulación en ningún otro lugar.

La solución es siempre verificar la firma contra el cuerpo de la solicitud sin procesar antes de que se produzca cualquier análisis o transformación.

Diferencias entre webhook de prueba y webhook en vivo

Otro problema común involucra las diferencias entre la función de webhook de prueba de Zendesk y las invocaciones de webhook en vivo. El formato de la carga útil puede variar ligeramente entre los dos, lo que hace que las firmas se validen en las pruebas pero fallen en la producción.

Comunidad de Zendesk
Me di cuenta de que cuando el webhook se ejecuta normalmente, la autenticación del mensaje falla. Así que copié el cuerpo del intento fallido, volví a 'Webhook de prueba', pegué el contenido en el cuerpo del mensaje, lo envié y la validación del mensaje ahora funciona de nuevo.

Siempre pruebe con invocaciones de webhook reales de eventos reales de Zendesk antes de implementar en producción.

Codificación de caracteres

Asegúrese de que tanto la marca de tiempo como el cuerpo se manejen como cadenas UTF-8 al concatenar para el cálculo de la firma. Las discrepancias de codificación entre su servidor y la carga útil de Zendesk provocarán fallos de verificación.

Validación de la marca de tiempo

Considere agregar la validación de la marca de tiempo para evitar ataques de repetición. Compruebe que la marca de tiempo en el encabezado esté dentro de una ventana razonable (por ejemplo, 5 minutos) de la hora actual. Las marcas de tiempo antiguas podrían indicar un ataque de repetición.

Cuándo regenerar secretos

Si sospecha que su secreto de firma ha sido comprometido, regenérelo inmediatamente a través del Centro de administración de Zendesk. Después de la regeneración, actualice su servidor con el nuevo secreto. Puede haber una breve ventana en la que los webhooks en vuelo utilicen el secreto anterior, así que considere admitir ambos durante la transición.

Probando su verificación de webhook

Antes de implementar en producción, pruebe a fondo su implementación de verificación de firma.

Usando el secreto de prueba estático

Durante el desarrollo, utilice el secreto de prueba estático de Zendesk (dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==) para verificar que su lógica de implementación sea correcta. Esto le permite probar sin crear un webhook en vivo.

Probando con la función de prueba de Zendesk

Cuando cree o edite un webhook en Zendesk, utilice el botón "Webhook de prueba" para enviar cargas útiles de prueba. Verifique que su punto final acepte estas solicitudes y valide la firma correctamente.

Pruebas en vivo

Cree un disparador real que invoque su webhook en un evento específico (como la creación de un ticket). Realice esa acción en Zendesk y confirme que su punto final recibe y verifica el webhook. Compruebe los registros de su servidor para detectar cualquier discrepancia en la firma.

Registro y depuración

Registre la siguiente información durante el desarrollo para ayudar a depurar los fallos:

  • Cuerpo de la solicitud sin procesar (antes del análisis)
  • Encabezado de firma recibido
  • Firma calculada
  • Encabezado de marca de tiempo

Nunca registre el secreto de firma en sí. Compare las firmas recibidas y calculadas carácter por carácter para identificar dónde divergen.

Flujo de trabajo de pruebas sistemáticas para la verificación de firmas de webhook
Flujo de trabajo de pruebas sistemáticas para la verificación de firmas de webhook

Asegure sus integraciones de Zendesk con eesel AI

La implementación correcta de la verificación de firmas de webhook requiere una atención cuidadosa a los detalles criptográficos, el manejo del cuerpo específico del marco de trabajo y los casos extremos en torno al formato JSON. Para los equipos que crean integraciones complejas, esta complejidad puede ralentizar el desarrollo e introducir riesgos de seguridad.

En eesel AI, hemos incorporado la seguridad de webhook directamente en nuestra integración de Zendesk. Cuando conecta su cuenta de Zendesk a nuestra plataforma, manejamos la verificación de firmas automáticamente. Obtiene los beneficios de seguridad sin escribir y mantener el código de verificación.

Panel de eesel AI para configurar el agente supervisor con una interfaz sin código
Panel de eesel AI para configurar el agente supervisor con una interfaz sin código

Nuestro Agente de IA para Zendesk va más allá, proporcionando una resolución autónoma de tickets mientras maneja toda la seguridad de webhook en segundo plano. Si está creando automatizaciones basadas en webhook y desea centrarse en la lógica empresarial en lugar de las implementaciones criptográficas, podemos ayudarle a simplificar su integración.

Preguntas Frecuentes

Sí, necesita acceso de administrador al Centro de administración de Zendesk para ver o regenerar los secretos de firma de webhook. Los secretos están ocultos por defecto y requieren hacer clic en 'Revelar secreto' para mostrarse.
No, cada webhook tiene su propio secreto de firma único generado cuando se crea. No puede compartir secretos entre webhooks, e intentar verificar la carga útil de un webhook con el secreto de otro webhook siempre fallará.
Primero, verifique que está utilizando el secreto de firma correcto (no el secreto de prueba estático para webhooks en vivo). Compruebe que está capturando el cuerpo de la solicitud sin procesar antes de cualquier análisis JSON. Asegúrese de que su concatenación de marca de tiempo y cuerpo coincida exactamente con el formato de Zendesk. Finalmente, verifique que no haya problemas de codificación con la carga útil.
Aunque no es estrictamente necesario, la verificación de firmas es altamente recomendada para cualquier integración de producción. Sin ella, su punto final es vulnerable a ataques de suplantación y repetición. Zendesk incluye los encabezados de firma en todos los webhooks, por lo que no hay desventajas en verificarlos.
Rote sus secretos de firma siempre que sospeche que están comprometidos, cuando los miembros del equipo con acceso se vayan, o como parte de una auditoría de seguridad regular (trimestral o anual). Recuerde que rotar un secreto requiere actualizar su código de verificación inmediatamente, ya que los nuevos webhooks usarán el nuevo secreto.
No, el secreto de prueba estático solo funciona con la función 'Webhook de prueba' de Zendesk durante la creación del webhook. Una vez que se crea un webhook, obtiene su propio secreto de firma único que debe usar para la verificación.
Cualquier lenguaje que admita el hash HMAC-SHA256 y la codificación Base64 puede verificar los webhooks de Zendesk. Las implementaciones más comunes utilizan Node.js, Python, PHP, Ruby, Java, C# y Go. Las operaciones criptográficas son estándar y están disponibles en la mayoría de las bibliotecas estándar de lenguajes.

Compartir esta entrada

Stevia undefined

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.