Sécuriser une API REST (2/3) : Implémentation en Node.js

Nous avons vu dans le précédent article, les recommandations concernant la sécurisation des échanges entre un client et une API REST. Nous allons donc aujourd’hui nous attaquer à l’implémentation de celles-ci en Node.js.

Avant de commencer…

Je tiens à m’excuser pour ces mois (années…) d’absences, mais comme je l’ai dit le blog n’est pas mort et je compte bien revenir pour cette année 2020, j’ai pas mal d’articles en cours de rédaction et je vais tenter d’être plus régulier. 

Dans cet article, je vais vous montrer comment sécuriser une API REST utilisant le framework Express. Je tiens à préciser que le but de cet article n’est pas de vous expliquer comment mettre en place un projet Express de A à Z, mais plutôt de se concentrer sur l’aspect sécurité. Par conséquent, nous nous concentrerons uniquement sur la mise en place du serveur HTTPS et de la sécurisation de nos routes en utilisant le standard JWT.

Bref, commençons !

Mise en place de HTTPS

Comme dit dans le précédent article, la première étape de la sécurisation d’une API REST est l’utilisation du protocole HTTPS. Pour cela, il nous faut obtenir un certificat SSL.

Plusieurs solutions s’offrent à nous :

  • Obtenir notre certificat SSL auprès d’une autorité de certification (pensez à Let’s Encrypt qui est gratuit);
  • Générer un certificat auto signé.

Pour cet article, nous allons générer un certificat auto signé.

Pour une mise en production, utiliser toujours un certificat obtenu auprès d’une autorité de certification.

Générer un certificat auto signé

Afin de générer notre certificat auto signé, nous avons besoin de OpenSLL et de lancer cette ligne de commande : 

Nous obtenons deux fichiers :

  • server.key qui contient la clé privée;
  • server.cert qui contient le certificat.

Création du serveur HTTPS

Il nous faut maintenant créer notre serveur HTTPS :

Voilà c’était simple non ? Notre serveur utilise maintenant le protocole HTTPS.

Il existe également une autre solution possible qui consiste à déporter la responsabilité de la communication HTTPS entre le client et le serveur à ce qu’on appelle un “reverse proxy“.

Un reverse proxy ? C’est quoi ?

Voyons la définition de notre cher Wikipédia :

Un proxy inverse (reverse proxy) est un type de serveur, habituellement placé en frontal de serveurs web. Contrairement au serveur proxy qui permet à un utilisateur d’accéder au réseau Internet, le proxy inverse permet à un utilisateur d’Internet d’accéder à des serveurs internes.

https://fr.wikipedia.org/wiki/Proxy_inverse

Bon, pour une fois c’est assez clair, le reverse proxy permet simplement de faire l’intermédiaire entre vos serveurs internes et un client.

 

Principe d'un reverse proxy

Principe d’un reverse proxy

Il existe plusieurs solutions de reverse proxy sur le marché comme HAProxy ou encore Traefik , mais la plus connue d’entre elles est nginx. Voyons rapidement comment la mettre en place.

Mise en place de nginx

Tout d’abord, commençons par l’installation de nginx:

Une fois installé, il nous faut créer un fichier contenant notre “server block” qui est l’équivalent des virtual hosts d’Apache :

Voici le contenu du fichier :

Pensez à modifier l’adresse et le port de votre API, votre nom de domaine, ainsi que le chemin d’accès de votre certificat et de votre clé privée.

Activons ensuite notre “server block” :

Pour terminer, redémarrons notre serveur nginx :

Votre API est maintenant accessible en HTTPS via votre nom de domaine.

Authentification via JWT

Pour commencer, il nous faut une route permettant aux utilisateurs de se connecter et récupérer un JWT. Nous aurons besoin de la librairie jsonwebtoken pour créer nos JWT :

Créons ensuite notre route de connexion :

L’étape suivante est de sécuriser nos routes pour permettre uniquement aux utilisateurs authentifiés d’y accéder. Imaginons que nous ayons la route suivante :

Comment protéger celle-ci à l’aide d’un JWT ? et bien nous allons utiliser un middleware.

Hey dis donc Jamy, c’est quoi un middleware ?

Un middleware est simplement une fonction permettant d’effectuer un traitement avant celui défini par les routes. Il possible de créer ce qu’on appelle une chaîne de middleware ou pipeline comme nous le montre le schéma suivant :

 

Chaîne de middleware Express

Chaîne de middleware

Chaque middleware, une fois son traitement terminé, peut soit faire appel au middleware suivant ou stopper la chaîne et envoyer une réponse au client.

La signature d’une fonction middleware est la suivante :

  • req : La requête HTTP du client;
  • res: La réponse HTTP à envoyer au client;
  • next: Un callback vers le prochain middleware;

Il existe un autre type de middleware permettant de traiter les erreurs qui possède la signature suivante :

Celui-ci peut être appelé en passant l’erreur en paramètre de la fonction next .

Middleware d’authentification

Revenons à nos moutons ! Voyons maintenant comment créer un middleware permettant de gérer l’authentification via les JWT et de renvoyer une erreur au client si l’authentification échoue.

Comme nous l’avons vu précédemment, un middleware permet d’accéder à la requête HTTP du client, nous devons donc récupérer le JWT présent dans les en-têtes de celle-ci, vérifier que celui-ci est valide et qu’il est bien associé à un utilisateur présent dans notre base de données :

Protégeons ensuite notre route. Pour cela il suffit d’appeler notre middleware juste avant le “handler” de notre route :

Notre route est maintenant protégée ! Rien de bien compliqué.

On a fini ?

Oui c’est terminé, ce n’était pas la mer à boire hein ? Comme je l’ai dit en introduction le but de l’article n’était pas de vous montrer comment créer une application Express de A à Z, mais de se concentrer sur la mise en place du protocole HTTPS et de la sécurisation de nos routes à l’aide de JWT. Certaines parties n’ont donc pas été présentées notamment celles concernant l’interaction avec la base de données ou encore la connexion d’un utilisateur et la génération d’un JWT associé, c’est pourquoi j’ai créé un projet complet sur github. N’hésitez surtout pas à aller voir et me poser des questions si besoin.

Des middlewares permettant de gérer l’authentification via JWT existent, comme par exemple express-jwt ou encore passport-jwt je vous invite grandement à aller y jeter un coup d’œil, la curiosité est la meilleure des qualités pour un développeur.

J’ai dit dans la première partie qu’il était également possible de protéger son API à l’aide d’une clé d’API donc pour celles et ceux que ça intéresse, j’ai écrit un middleware disponible sur github. 

Bon, on a pas encore fini avec la sécurité d’une API REST, il y aura une partie 3 à cet article qui portera sur le stockage des JWT notamment coté front cela parlera de failles XSS et CSRF je n’en dis pas plus… par contre je ne sais pas encore pour quand c’est prévu, mais promis pas dans 2 ans ! Entre-temps d’autres articles arriveront. On se retrouve bientôt ! 

 


Annonces partenaire

Je suis lead developer dans une boîte spécialisée dans l'univers du streaming/gaming, et en parallèle, je m'éclate en tant que freelance. Passionné par l'écosystème JavaScript, je suis un inconditionnel de Node.js depuis 2011. J'adore échanger sur les nouvelles tendances et partager mon expérience avec les autres développeurs. Si vous avez envie de papoter, n'hésitez pas à me retrouver sur Twitter, m'envoyer un petit email ou même laisser un commentaire.

4 commentaires

  1. Sans votre fichier model ainsi que votre fichier config c’est compliqué de comprendre, j’avou etre un peu perdu du coup sur vos méthode

    1. J’ai volontairement simplifié les exemples pour ne pas surcharger l’article avec la gestion de la BDD qui peut être différente suivant les projets (utilisation de MySQL ou de MongoDB, utilisation d’un ORM ou non, etc.). J’ai malgré tout mis à disposition sur Github (https://github.com/arkerone/express-security-example) un projet qui met en œuvre ce qui est expliqué dans l’article.

  2. Super Article et exemple de code, notamment sur Github, magnifique ! Je m’inspire de ton approche model/controller pour construire ma nouvelle codebase backend. J’utilise Fastify en lieu et place d’Express par contre ainsi que Redis en DB. Pour la partie frontend de ma codebase j’ai choisi Svelte . Encore merci pour ton travail 🙂

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.