Lorsque vous créez des intégrations qui reçoivent des webhooks de Zendesk, vous ouvrez un point de terminaison sur votre serveur qui accepte les requêtes HTTP provenant d’Internet. Sans vérification appropriée, n’importe qui pourrait envoyer de fausses requêtes à ce point de terminaison et potentiellement déclencher des actions indésirables dans votre système. C’est là que la vérification de la signature entre en jeu.
La vérification de la signature du webhook Zendesk vous offre un moyen de prouver cryptographiquement que les webhooks entrants proviennent réellement de Zendesk et n’ont pas été altérés en transit. Ce guide vous explique tout ce dont vous avez besoin pour l’implémenter correctement, avec des exemples de code fonctionnels dans cinq langages de programmation populaires.
Si vous recherchez des conseils plus généraux sur la configuration des webhooks dans Zendesk, notre guide de configuration des webhooks de messagerie Zendesk couvre l’ensemble du processus de configuration.

Qu’est-ce que la vérification de la signature du webhook et pourquoi est-ce important ?
La vérification de la signature du webhook est un mécanisme de sécurité qui vous permet de confirmer l’authenticité des requêtes de webhook entrantes. Lorsque Zendesk envoie un webhook à votre point de terminaison, il inclut une signature cryptographique que seul Zendesk aurait pu générer. Votre serveur recalcule cette signature à l’aide d’un secret partagé et compare les résultats. S’ils correspondent, le webhook est authentique.
Sans cette vérification, votre point de terminaison est vulnérable à plusieurs attaques :
- Usurpation : Quiconque découvre l’URL de votre webhook pourrait envoyer de fausses requêtes en se faisant passer pour Zendesk
- Attaques de relecture : Un attaquant pourrait capturer un webhook légitime et le renvoyer plusieurs fois
- Altération de la charge utile : Les données de requête pourraient être modifiées en transit sans être détectées
Pour les intégrations de production qui traitent des données de ticket sensibles ou qui déclenchent des flux de travail automatisés, la vérification de la signature n’est pas facultative. Il s’agit d’un contrôle de sécurité fondamental qui protège à la fois votre système et les données de vos clients.
Chez eesel AI, nous gérons automatiquement la sécurité des webhooks lorsque vous connectez votre compte Zendesk. Notre plateforme vérifie les signatures de manière transparente afin que vous puissiez vous concentrer sur la création d’automatisations plutôt que sur les implémentations cryptographiques.
Comment fonctionnent les signatures de webhook Zendesk
Zendesk utilise l’algorithme SHA256 HMAC (Hash-based Message Authentication Code, code d’authentification de message basé sur le hachage) pour générer des signatures de webhook. Le processus combine le secret de signature de votre webhook avec la charge utile de la requête et l’horodatage pour créer une signature unique pour chaque requête.
La formule ressemble à ceci :
base64(HMACSHA256(TIMESTAMP + BODY))
Voici ce qui se passe lorsque Zendesk envoie un webhook :
- Zendesk concatène l’horodatage et le corps de la requête brute en une seule chaîne
- Il crée un hachage HMAC-SHA256 à l’aide du secret de signature de votre webhook comme clé
- Le hachage est codé en Base64 pour produire la signature finale
- Zendesk envoie le webhook avec deux en-têtes essentiels :
X-Zendesk-Webhook-Signature: la signature généréeX-Zendesk-Webhook-Signature-Timestamp: l’horodatage utilisé dans la signature
Chaque requête de webhook de Zendesk inclut ces en-têtes standard :
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
Sur votre serveur, vous extrayez ces en-têtes, recalculez la signature à l’aide de votre secret de signature stocké et comparez les résultats. Si les signatures correspondent, vous pouvez être sûr que le webhook provient de Zendesk et que la charge utile n’a pas été modifiée.
Récupération de votre secret de signature de webhook
Avant de pouvoir vérifier les signatures, vous avez besoin du secret de signature de votre webhook. Chaque webhook dans Zendesk possède son propre secret unique qui est généré lors de la création du webhook.
Trouver votre secret dans le Centre d’administration
- Accédez au Centre d’administration Zendesk (Centre d’administration > Applications et intégrations > Webhooks)
- Sélectionnez le webhook que vous souhaitez vérifier
- Sur la page des détails du webhook, recherchez le champ du secret de signature
- Cliquez sur « Révéler le secret » pour afficher la valeur

Traitez ce secret comme n’importe quel autre identifiant. Ne l’intégrez pas au code, ne l’exposez pas dans les applications côté client et limitez-en l’accès au sein de votre équipe.
Récupération via l’API
Vous pouvez également récupérer le secret de signature par programmation à l’aide de l’API Afficher le secret de signature du webhook :
GET /api/v2/webhooks/{webhook_id}/signing_secret
Secret de test statique pour le développement
Lorsque vous testez des webhooks avant de les créer dans Zendesk, vous aurez besoin d’un secret de signature statique, car les secrets réels ne sont générés qu’après la création du webhook. Utilisez ce secret de test pendant le développement :
dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==
Une fois votre webhook créé, passez au secret de signature réel. Les webhooks de test et les webhooks en direct utilisent des secrets différents, votre code de vérification doit donc gérer le secret correct pour chaque environnement.

Guide d’implémentation étape par étape
L’implémentation de la vérification de la signature implique quatre étapes clés. Décomposons chacune d’elles.
Étape 1 : Capturez le corps de la requête brute
La signature est calculée sur le corps de la requête brute sous forme de chaîne, et non sur les données JSON ou de formulaire analysées. Si votre framework analyse le corps avant que vous puissiez y accéder, la vérification de la signature échouera car les octets bruts ont été transformés.
La plupart des frameworks Web fournissent des options de middleware ou de configuration pour capturer le corps brut avant l’analyse. Vous devez généralement stocker le corps brut dans une propriété telle que req.rawBody afin qu’il soit disponible pour le calcul de la signature.
Piège courant : Le middleware d’analyse du corps (comme express.json() d’Express) s’exécute souvent avant votre gestionnaire de route. Si le corps est analysé en un objet JavaScript avant que vous ne capturiez la chaîne brute, vous ne pouvez pas récupérer les octets d’origine pour la vérification de la signature. Configurez votre middleware pour capturer d’abord le corps brut.
Étape 2 : Extrayez les en-têtes de signature
Extrayez les deux en-têtes liés à la signature de la requête entrante :
X-Zendesk-Webhook-Signature: la signature à vérifierX-Zendesk-Webhook-Signature-Timestamp: l’horodatage utilisé dans le calcul de la signature
Notez que certains frameworks transforment les noms d’en-tête. Dans Ruby on Rails, par exemple, l’en-tête X-Zendesk-Webhook-Signature devient HTTP_X_ZENDESK_WEBHOOK_SIGNATURE dans l’environnement de requête.
Étape 3 : Calculez la signature attendue
Concaténez l’horodatage et le corps brut, puis créez un hachage HMAC-SHA256 à l’aide de votre secret de signature :
- Créez une chaîne :
timestamp + body(horodatage d’abord, puis le corps brut) - Générez HMAC-SHA256 à l’aide de votre secret de signature comme clé
- Encodez le hachage résultant en Base64
Cette signature calculée doit correspondre à celle que Zendesk a envoyée dans l’en-tête X-Zendesk-Webhook-Signature.
Étape 4 : Comparez les signatures en toute sécurité
Utilisez une fonction de comparaison à temps constant pour comparer les signatures. La comparaison de chaînes régulière (== ou ===) peut divulguer des informations sur la signature par le biais d’une analyse temporelle, ce qui pourrait théoriquement aider un attaquant à forger des signatures valides.
La plupart des langages fournissent une fonction de comparaison à temps constant :
- Node.js :
crypto.timingSafeEqual() - Python :
hmac.compare_digest() - PHP :
hash_equals() - Ruby :
ActiveSupport::SecurityUtils.secure_compare() - C# : Pas de comparaison à temps constant intégrée, mais
CryptographicOperations.FixedTimeEquals()dans .NET Core
Si les signatures correspondent, traitez le webhook. Si elles ne correspondent pas, renvoyez une réponse 401 Non autorisé et enregistrez l’échec pour enquête.
Exemples de code dans les langages populaires
Voici des implémentations complètes et fonctionnelles pour les langages de développement Web les plus courants.
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();
}
}
Problèmes courants et dépannage
Même avec le bon code, la vérification de la signature peut échouer pour des raisons subtiles. Voici les problèmes les plus courants que rencontrent les développeurs.
Espacement et formatage JSON
L’un des problèmes les plus frustrants concerne le formatage JSON. La signature est calculée sur les octets exacts que Zendesk envoie, y compris les espaces blancs. Si votre framework reformate le JSON (en ajoutant ou en supprimant des espaces), la signature ne correspondra pas.
Un développeur de la communauté Zendesk l’a découvert à ses dépens :
La solution consiste à toujours vérifier la signature par rapport au corps de la requête brute avant toute analyse ou transformation.
Différences entre le webhook de test et le webhook en direct
Un autre problème courant concerne les différences entre la fonctionnalité de webhook de test de Zendesk et les appels de webhook en direct. Le format de la charge utile peut varier légèrement entre les deux, ce qui entraîne la validation des signatures lors des tests, mais l’échec en production.
Testez toujours avec des appels de webhook réels à partir d’événements Zendesk réels avant de déployer en production.
Encodage des caractères
Assurez-vous que l’horodatage et le corps sont gérés comme des chaînes UTF-8 lors de la concaténation pour le calcul de la signature. Les incompatibilités d’encodage entre votre serveur et la charge utile de Zendesk entraîneront des échecs de vérification.
Validation de l’horodatage
Envisagez d’ajouter une validation d’horodatage pour empêcher les attaques de relecture. Vérifiez que l’horodatage dans l’en-tête se trouve dans une fenêtre raisonnable (par exemple, 5 minutes) de l’heure actuelle. Les anciens horodatages pourraient indiquer une attaque de relecture.
Quand régénérer les secrets
Si vous soupçonnez que votre secret de signature a été compromis, régénérez-le immédiatement via le Centre d’administration Zendesk. Après la régénération, mettez à jour votre serveur avec le nouveau secret. Il peut y avoir une brève fenêtre où les webhooks en vol utilisent l’ancien secret, alors envisagez de prendre en charge les deux pendant la transition.
Tester votre vérification de webhook
Avant de déployer en production, testez minutieusement votre implémentation de vérification de la signature.
Utilisation du secret de test statique
Pendant le développement, utilisez le secret de test statique de Zendesk (dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==) pour vérifier que votre logique d’implémentation est correcte. Cela vous permet de tester sans créer de webhook en direct.
Test avec la fonctionnalité de test de Zendesk
Lorsque vous créez ou modifiez un webhook dans Zendesk, utilisez le bouton « Tester le webhook » pour envoyer des charges utiles de test. Vérifiez que votre point de terminaison accepte ces requêtes et valide correctement la signature.
Tests en direct
Créez un déclencheur réel qui appelle votre webhook sur un événement spécifique (comme la création d’un ticket). Effectuez cette action dans Zendesk et confirmez que votre point de terminaison reçoit et vérifie le webhook. Vérifiez vos journaux de serveur pour toute incompatibilité de signature.
Journalisation et débogage
Consignez les informations suivantes pendant le développement pour faciliter le débogage des échecs :
- Corps de la requête brute (avant l’analyse)
- En-tête de signature reçu
- Signature calculée
- En-tête d’horodatage
Ne consignez jamais le secret de signature lui-même. Comparez les signatures reçues et calculées caractère par caractère pour identifier où elles divergent.
Sécurisez vos intégrations Zendesk avec eesel AI
L’implémentation correcte de la vérification de la signature du webhook nécessite une attention particulière aux détails cryptographiques, à la gestion du corps spécifique au framework et aux cas extrêmes concernant le formatage JSON. Pour les équipes qui créent des intégrations complexes, cette complexité peut ralentir le développement et introduire des risques de sécurité.
Chez eesel AI, nous avons intégré la sécurité des webhooks directement dans notre intégration Zendesk. Lorsque vous connectez votre compte Zendesk à notre plateforme, nous gérons automatiquement la vérification de la signature. Vous bénéficiez des avantages de la sécurité sans écrire ni maintenir de code de vérification.

Notre agent d’IA pour Zendesk va plus loin, en fournissant une résolution autonome des tickets tout en gérant toute la sécurité des webhooks en arrière-plan. Si vous créez des automatisations basées sur des webhooks et que vous souhaitez vous concentrer sur la logique métier plutôt que sur les implémentations cryptographiques, nous pouvons vous aider à simplifier votre intégration.
Foire aux questions
Partager cet 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.



