
Partager:
Ancien développeur .NET Advocate @Vonage, ingénieur logiciel polyglotte full-stack, AI/ML
Validation des messages entrants à partir de l'API Messages de Vonage
Temps de lecture : 7 minutes
Introduction aux Webhooks
L'API Vonage Messages API est phénoménale. Je sais qu'en tant que défenseurs, nous ne sommes pas censés faire de favoritisme, mais c'est en fait mon API préféré parmi ceux de Vonage. Elle vous permet d'envoyer messages sur WhatsApp, Facebook Messenger, Viber, SMS et MMS très facilement. Et à l'autre bout, elle vous permet de recevoir messages sur la plupart de ces canaux (les messages SMS sont renvoyés aux points de terminaison de votre Account).
Pour que votre application reçoive un message entrant (ou l'état d'un message sortant) de Vonage, elle doit disposer d'un point de terminaison HTTP accessible au public auquel Vonage peut envoyer le message. Ce message est appelé "webhook". Nous avons tendance à voir beaucoup de questions sur la sécurité et les webhooks, c'est pourquoi je vais vous montrer comment vous pouvez sécuriser vos webhooks Messages. Nous allons y parvenir en combinant l'authentification du porteur et la validation de la charge utile.
Inquiétudes concernant la sécurité de Webhook
La plus grande question que nous recevons à propos des webhooks, au-delà de ce qu'ils sont, est de savoir comment sécuriser les webhooks pour s'assurer que les mauvais acteurs n'envoient pas de webhooks malveillants. Que peut faire un attaquant s'il accède à nos points d'extrémité de webhook et qu'il dispose d'une masse critique de données utilisateur à nous envoyer ? C'est une préoccupation légitime.
Pensez-y comme suit : Alice est une développeuse d'applications, et elle a besoin de recevoir un message WhatsApp de Bob. Bob enverra un message WhatsApp à l'application d'Alice. Lorsque ce message WhatsApp est reçu, Vonage envoie un message au point de terminaison HTTP d'Alice pour informer son application du message entrant. Ce point d'accès doit être accessible au public. Si Chuck, un pirate malveillant, trouve le point de terminaison webhook d'Alice, il peut maintenant se faire passer pour Bob pour les messages entrants ou falsifier les statuts des messages sortants.
Authentifier les Webhooks entrants
Vonage utilise JSON Web Token (JWT) Bearer Authorization (autorisation du porteur) pour les webhooks envoyés depuis l'API Messages afin de vous permettre d'authentifier facilement le webhook. Le jeton porteur est un jeton HMAC-SHA256, ce qui signifie que la vérification de la validité du JWT est aussi simple que le décodage du jeton avec son secret de signature. Le secret que vous devez utiliser est le même que celui indiqué dans la page dans le tableau de bord. Il est recommandé d'utiliser un secret de signature d'au moins 32 bits afin de rendre difficile une attaque par force brute. Le secret de signature de votre Account est un secret partagé entre vous et Vonage.
signing secret
Valider la charge utile d'un Webhook entrant
Outre l'autorisation du jeton, il convient de vérifier que la charge utile du webhook correspond à ce que le jeton indique. Les JWT, une fois décodés, ont leur propre charge utile JSON, dont les champs sont appelés "claims". Pour éviter qu'un attaquant ne vole l'un de vos jetons et ne le rejoue, vous pouvez utiliser l'une de ces revendications, l'expression "claim". payload_hash. Le champ payload_hash est un hachage SHA-256 de la charge utile du webhook. Il suffit de soumettre le contenu du message entrant à un hachage SHA-256 et de le comparer à l'allégation payload_hash dans le webhook, vous pouvez être sûr que le jeton que vous recevez n'est pas une relecture.
Vérifier l'heure à laquelle un jeton a été généré
Un autre élément important est la mention "-this" qui signifie "issued at" (émis à). iat-this signifie "issued at" (émis à) et correspond à l'horodatage Unix UTC de la date à laquelle le jeton a été généré. Vous pouvez comparer le iat à l'horodatage Unix UTC actuel pour vérifier l'ancienneté de l'horodatage afin de se prémunir contre les jetons potentiellement périmés.
Traduire en code
Prenons ces concepts et convertissons-les en code. Je vais vous montrer comment le faire en Node.js, mais ces techniques sont disponibles dans pratiquement tous les langages de programmation.
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Rassembler les dépendances
Créez un nouveau répertoire appelé signed_webhooks et cd dans ce répertoire. Dans ce répertoire, exécutez npm install dotenv jsonwebtoken js-sha256 express body-parser.
Créer des fichiers et ajouter une configuration
Dans notre signed_webhooks nous allons créer un répertoire server.js et un fichier .env et un fichier Le fichier server.js est l'endroit où notre code serveur va aller et notre fichier .env est l'endroit où notre configuration va vivre. Dans le fichier .env ajoutez un seul champ, NEXMO_API_SIGNATURE_SECRETet définissez-le comme étant le secret de signature de votre paramètres du tableau de bord de votre tableau de bord.
Initialiser les dépendances
Maintenant que nous avons rassemblé toutes nos dépendances et configuré notre serveur, nous devons ajouter le code du serveur. Commençons par initialiser nos dépendances. Ajoutez ce qui suit à server.js :
require('dotenv').config();
const jwt = require("jsonwebtoken");
const sha256 = require('js-sha256');
const app = require('express')();
const bodyParser = require('body-parser');
const NEXMO_API_SIGNATURE_SECRET = process.env.NEXMO_API_SIGNATURE_SECRET;
if(!NEXMO_API_SIGNATURE_SECRET){
throw "Missing Signature Secret";
}Ce code va intégrer toutes nos dépendances et extraire le secret de signature de notre environnement.
Ajouter une route pour les messages entrants
Ensuite, nous devons mettre en place la route pour inbound-message et status. Nous supposons que les webhooks que nous recevons seront des requêtes de type POST nous allons donc ajouter une route vers /webhooks/inbound-message et /webhooks/status et et configurerons POST pour qu'elles soient acheminées par notre fonction handleWebhook fonction.
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app
.route('/webhooks/inbound-message')
.post(handleWebhook);
app
.route('webhooks/status')
.post(handleWebhook) Traiter les messages entrants
Lorsque nous recevons un message entrant, nous extrayons le corps du message et l'ajoutons à la charge utile. Ensuite, nous séparons l'en-tête d'autorisation sur l'espace blanc (l'en-tête d'autorisation sera de la forme "Bearer Token", donc la séparation sur l'espace blanc et la prise de la partie du jeton nous donnera le JWT).
Une fois le jeton récupéré, nous pouvons décoder le JWT à l'aide de notre signature secrète. Comme nous l'avons vu précédemment, cette action de décodage équivaut à valider l'authenticité du jeton. Si le JWT n'est pas valide ou si l'en-tête auth est malformé, une exception sera levée par l'opération de décodage, et nous renverrons un 401. Si nous sommes en mesure de décoder le jeton, nous avons vérifié l'authenticité du jeton - ainsi, si vous êtes derrière TLS et que vous ne vous souciez pas de valider la charge utile, vous pouvez en toute sécurité renvoyer un 200 à ce stade. Si, par contre, vous choisissez de vérifier la charge utile, il suffit d'exécuter une commande JSON.stringify sur la charge utile et de la comparer à l'allégation de la JWT décodée. payload_hash de la JWT décodée. Si vous détectez une falsification, vous pouvez renvoyer un 401 à partir du point de terminaison pour dire aux gens qu'ils ne sont pas autorisés. Enfin, nous allons dire à notre application d'écouter sur un port spécifié ou sur le port 3000.
Tout ceci est réalisé dans la demande suivante handleWebhook demande :
function handleWebhook(request, response){
const payload = Object.assign(request.query, request.body)
try{
let token = request.headers.authorization.split(" ")[1]
var decoded = jwt.verify(token, NEXMO_API_SIGNATURE_SECRET, {algorithms:['HS256']});
if(sha256(JSON.stringify(payload))!=decoded["payload_hash"]){
console.log("tampering detected");
response.status(401).send();
}
else{
console.log("Success");
response.status(204).send();
}
}
catch(err){
console.log('Bad token detected')
response.status(401).send()
}
}
app.listen(process.env.PORT || 3000) Essais
À des fins de test, nous allons nous contenter de l'exécuter localement. Le démarrage du serveur est simple : exécutez node server.jset le serveur démarre.
Mise en place de ngrok
Pour acheminer les webhooks vers notre serveur, nous allons utiliser ngrok. Ngrok nous permet de créer un tunnel pour notre serveur local. Exécutez la commande suivante.
Cela ouvrira une session ngrok qui ressemblera à ce qui suit http://random.ngrok.io-swap out random avec le hash aléatoire au début de l'URL, puis ajouter la route à /webhooks/inbound-message et vous aurez l'URL pour vos webhooks.
ngrok
Configurer les Webhooks
Maintenant que nous disposons d'un tunnel vers notre serveur, la dernière chose à faire avant de recevoir des messages est de configurer nos webhooks. À des fins de test, vous pouvez utiliser le bac à sable de l'API Messages - les instructions peuvent être trouvées dans l'article de blog de Martyn de Martyn sur le sujet ou dans la documentation sur l'API Sandbox de Messages API Sandbox docs.
Pour la production, vous devrez configurer les webhooks de votre application. Vous pouvez le faire à l'adresse ${CUSTOMER_DASHBOARD_URL}/applications/:appid/editen remplaçant :appid par l'identifiant de votre application. Vous pouvez également les configurer à l'aide de la CLI Nexmo ou de l Application API.
Après modification, la configuration du webhook de votre application pour les messages doit ressembler à ceci :
application webhooks
Maintenant que tout est configuré, nous pouvons tester l'envoi d'un message à votre numéro WhatsApp, Viber ou Facebook Messenger, et votre serveur validera les webhooks entrants !
Validation des SMS entrants
La validation des SMS entrants n'entre pas dans le cadre de cet article, mais nous disposons d'une méthodologie similaire pour valider les SMS entrants. Vous pouvez lire comment dans notre Documentation pour les développeurs.
Ressources
Le code de cet article est disponible sur GitHub.
Une explication plus détaillée du fonctionnement de l'authentification JWT est disponible dans nos documentation pour les développeurs.
Si vous avez un JWT que vous voulez décoder manuellement, vous pouvez facilement le faire avec jwt.io de jwt.io.
