L’importance de vérifier les signatures des webhooks
Marcelo Oliveira
29 juin 2023
0 minutes de lectureLes webhooks sont une technique d’intégration par rappel qui permet d’envoyer et de recevoir des informations, comme des notifications d’événements, presque en temps réel. Ils peuvent être déclenchés par des événements d’application et transmettre des données via HTTP à une autre application ou à une API tierce. Vous pouvez configurer une URL de webhook et connecter des intervenants externes pour personnaliser, étendre ou modifier vos workflows.
Les webhooks peuvent être signés ou non. Toutefois, pour des raisons de sécurité, il est recommandé d’inclure une signature vérifiable afin que le destinataire puisse confirmer que la requête provient bien de la source de webhook attendue.
Voici quelques exemples d’utilisation des webhooks aujourd’hui :
Notifications sur une plateforme de gestion de versions basée sur Git lorsqu’un développeur a poussé du code vers le dépôt.
Notifications lorsqu’un utilisateur répond à une conversation sur une plateforme de messagerie.
Notifications d’un service de paiement confirmant l’autorisation d’un moyen de paiement pour un achat en magasin.
Selon un rapport de Verizon, les attaques contre la chaîne d’approvisionnement représentaient 62 % des incidents d’intrusion dans les systèmes en 2022. Tout au long du cycle de vie du développement logiciel, il est essentiel de sécuriser tous les composants et toutes les pratiques impliqués dans le développement et le déploiement. Si des vulnérabilités sont exploitées, elles peuvent compromettre les partenaires concernés et perturber l’ensemble de la chaîne d’approvisionnement logicielle.
Pour contrer ces menaces et sécuriser la chaîne d’approvisionnement, les fournisseurs imposent des signatures de webhook. Par exemple, avec CircleCI, vous pouvez utiliser des signatures de webhook pour valider un secret et vous assurer que seul CircleCI — et non un acteur malveillant — appelle votre webhook. Travis CI fournit également un en-tête HTTP de signature dans le cadre de son processus de vérification des webhooks.
Dans ce tutoriel, nous allons mettre en place un webhook GitHub en Node.js qui détecte les pushs de code vers un dépôt.
Prérequis
Pour suivre ce tutoriel, vous devez :
Télécharger et installer Node.js et npm.
Installer ngrok, un outil de proxy inverse qui crée un tunnel sécurisé entre une URL publique et votre application Node.js locale.
Installer Postman, une plateforme qui vous permet de créer des requêtes HTTP et d’en examiner facilement les résultats.
Vérifier les signatures de webhook avec Node.js
Créez un dossier sur votre ordinateur, puis un fichier nommé webhook.js contenant le code suivant. Pensez à remplacer la valeur de la constante repo par le chemin de votre dépôt local :
Exécutez ensuite la commande suivante dans votre terminal :
Voici ce que nous cherchons à réaliser avec le fichier webhook.js :
La constante
repocontient le chemin du dossier qui héberge le dépôt que nous voulons surveiller.La fonction
createServerdémarre un serveur HTTP sur notre machine, sur le port 8080. L’application de webhook est maintenant en cours d’exécution et prête à recevoir des requêtes à l’adresse http://localhost:8080.Une fois la requête entrante traitée, la constante body en contient l’essentiel. Une requête adressée à notre endpoint signifie que nous recevons une notification du webhook GitHub à l’URL de payload locale http://localhost:8080, nous informant qu’un utilisateur a poussé du code vers le dépôt que nous surveillons.
Nous voulons nous assurer que le push a bien été effectué sur la branche main. Pour le vérifier, nous analysons les données JSON de la requête dans un objet body et utilisons la constante
isMainpour indiquer si la référence correspond à la branche main.Si la requête est acceptée, nous exécutons une commande pour nous placer dans le dépôt local. Enfin, nous exécutons les commandes
git fetchetgit pullpour le mettre à jour.
Simuler une requête malveillante avec Postman
Nous concevons l’application Node.js pour qu’elle fonctionne avec GitHub. Toutefois, avant d’utiliser GitHub, simulons une requête malveillante avec Postman afin de montrer la vulnérabilité de notre application. Vous comprendrez ainsi l’importance des signatures.
Ouvrez l’application Postman et configurez une nouvelle requête HTTP POST vers notre URL de payload locale http://localhost:8080.
Dans l’onglet Body, ajoutez le code suivant :

Cliquez sur Send. Ouvrez le terminal où Node.js est en cours d’exécution et observez le résultat :

Comme vous pouvez le constater, l’application Node.js accepte une requête provenant d’une source autre que GitHub, ce qui est dangereux. Nous devons intégrer des signatures de webhook à notre code pour vérifier la légitimité de l’expéditeur.
Configurer ngrok
Dans cet exemple, l’application Node.js s’exécute en local. Cependant, GitHub ne peut pas accéder à l’endpoint http://localhost:8080 sur votre machine pour envoyer une requête de webhook. Nous pourrions résoudre ce problème en publiant l’application Node.js sur un serveur public. Mais il est plus facile d’exécuter et de déboguer Node.js sur la machine de développement que de le déployer sur un serveur Web ou dans le cloud. Nous allons donc utiliser ngrok. ngrok est un outil de proxy inverse qui crée un tunnel sécurisé entre une URL publique aléatoire et votre endpoint de webhook local.
Ouvrez un terminal sur votre machine et exécutez la commande ngrok suivante pour exposer le port local 8080 :
ngrok confirme que le port local 8080 est exposé via un tunnel sécurisé associé à une URL publique aléatoire :
Nous allons ensuite utiliser le sous-domaine ngrok public ci-dessus pour configurer un webhook GitHub.
Configurer un webhook GitHub
Rendez-vous sur la page GitHub de création d’un dépôt et ajoutez un nouveau dépôt pour configurer le webhook :

Cliquez sur Settings dans la barre supérieure. Sélectionnez ensuite l’option Webhooks dans la liste de gauche, puis cliquez sur Add webhook :

Le formulaire Add webhook s’ouvre. Nous allons y configurer un webhook déclenché lorsqu’un développeur pousse du code vers le dépôt.

Remplissez le formulaire comme suit :
Dans le champ Payload URL, saisissez l’adresse ngrok correspondant à l’endpoint de votre application Node.js locale.
Dans le champ Content type, sélectionnez « application/json » pour indiquer le type de contenu que GitHub enverra à l’endpoint.
Dans le champ Secret, saisissez « some-webhook-secret ». En production, vous devrez utiliser des mots de passe ou des phrases secrètes plus robustes pour garantir la sécurité.
Sous Which events would you like to trigger this webhook, sélectionnez Just the push event. Le webhook GitHub sera ainsi configuré pour l’événement de push vers un dépôt, que vous pourrez déclencher pour tester la vérification.
Remarque : Il est toujours conseillé d’activer la vérification SSL (Secure Socket Layer) dans la configuration du webhook. Elle permet aux organisations de s’assurer que les données transmises entre les systèmes sont sécurisées et ne peuvent pas être interceptées par des personnes non autorisées.
Une fois le formulaire rempli, cliquez sur Add webhook.

Sécuriser le webhook
Nous devons ajouter le module crypto de Node.js pour traiter les données chiffrées. Ajoutez cette ligne au début du fichier webhook.js :
Déclarez ensuite une constante contenant la même chaîne secrète que celle configurée dans votre webhook GitHub :
Remarque : Il est fortement déconseillé d’inscrire vos secrets en dur dans le code. Cette méthode est présentée à des fins de démonstration uniquement.
Au début de la fonction req.on, nous déclarons une constante contenant la valeur de la signature SHA256 calculée à l’aide de HMAC. Obtenez la signature HMAC-SHA256 en exécutant une fonction de hachage cryptographique SHA256 sur la clé secrète partagée et la valeur transmise par la requête GitHub (représentée ici par le paramètre chunk) :
Nous déclarons ensuite la constante isAllowed dans l’événement req.on pour indiquer si l’en-tête x-hub-signature-256 correspond à la signature calculée. Utilisez la fonction crypto.timingSafeEqual, qui compare deux variables sans révéler d’informations de timing susceptibles d’aider un attaquant à deviner l’une des valeurs :
Dans le code ci-dessus, une correspondance signifie que nous pouvons être certains que la requête provient de GitHub et non d’un acteur malveillant. Si la requête ne provient pas de GitHub, le serveur renvoie une réponse HTTP 401 Unauthorized.
Voici quelques bonnes pratiques appliquées dans ce code, que vous devriez suivre lors du développement de vos webhooks :
Pour calculer les signatures de webhook à l’aide d’opérations sur des chaînes, traitez toujours le payload au format UTF-8, car il peut contenir des caractères Unicode. De plus, si vous savez à quoi vos données doivent ressembler, vous pouvez mettre en place une méthode de validation pour vérifier que les données reçues sont conformes aux attentes.
Certains fournisseurs de webhooks, comme GitHub, peuvent inclure un en-tête
x-hub-signaturedans leurs requêtes. Cet en-têtex-hub-signatureest généré avec SHA-1, un algorithme de hachage considéré comme faible, mais il est conservé pour assurer la rétrocompatibilité. Dans la mesure du possible, utilisez l’en-têtex-hub-signature-256, généré avec l’algorithme robuste SHA-256.Commencez la signature de hachage par
"sha256=", en utilisant le secret du corps du payload de la requête.Au lieu d’utiliser un opérateur d’égalité simple (
"=="ou"==="), utilisez une méthode de comparaison sécurisée qui réduit le risque d’attaque par mesure du temps. Tous les langages et toutes les plateformes modernes proposent au moins une méthode de ce type. Dans ce projet Node.js, nous utilisons la fonctioncrypto.timingSafeEqual.
Vous trouverez le code complet de webhook.js ici.
Pour en savoir plus, consultez la documentation GitHub sur la sécurisation des webhooks.
Tester l’endpoint de webhook sécurisé
Comme vous avez modifié le code, vous devez redémarrer votre serveur Node local. Dans votre console, arrêtez le serveur Node avec Ctrl+C (ou Command+C sur Mac), puis exécutez de nouveau la commande node webhook.js.
Retournez ensuite dans Postman et renvoyez la requête. L’application Node.js répond maintenant avec le code HTTP 401 Unauthorized :

Ouvrez GitHub et ajoutez un fichier README.md si ce n’est pas déjà fait, ou ouvrez-le s’il existe. Ajoutez ou modifiez ensuite le texte comme indiqué ci-dessous :

Enfin, cliquez sur Commit changes pour pousser les modifications vers la branche main du dépôt :

Retournez maintenant dans le terminal Node.js pour vérifier que le dépôt a bien été récupéré et mis à jour :

Conclusion
Les webhooks permettent aux outils de communiquer et de transmettre des données en temps réel lorsque des événements se produisent. Vous pouvez les intégrer à vos systèmes pour créer des workflows collaboratifs flexibles et étendus, impossibles à mettre en place avec des API aux paramètres prédéfinis.
Comme ils sont ouverts par conception, les webhooks sont exposés aux menaces de sécurité. Il est donc essentiel de prendre les mesures nécessaires pour prévenir les attaques contre la chaîne d’approvisionnement.
Les signatures de webhook sont la solution recommandée : les fournisseurs peuvent configurer des secrets et générer des signatures à l’aide d’algorithmes de hachage robustes. Elles constituent un moyen simple et sûr de garantir que les échanges de données par webhook ont lieu uniquement entre des partenaires vérifiés et de confiance.
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.



