Sécurisez vos routes Node

Sécurisez vos routes Node

Quand on lance notre application Node et que celle-ci partage des informations via ses routes API, on est toujours tout content et on passe généralement à autre chose. Mais quand on passe en production et que notre application est utilisée par des milliers de personnes, il vaut mieux avoir quelques bases en ce qui concerne la sécurité. Sinon PAF, plus de serveur.

Ryan Djoher

03/03/2021 - 5 minutes de lecture

Internet est une jungle dont les arbres peuvent parfois cacher de vilaines personnes qui n’hésiteront pas à faire tomber votre application Node. Et ça c’est dans les meilleurs des cas. Si ces personnes sont vraiment malveillantes, ils peuvent voler pas mal d’informations qui ne devrait pas être disponible à qui veut. Cet article vous donnera quelques astuces afin de vous protéger et ne pas se retrouver avec une application prise d’assaut par des hackers. Bien entendu, si vous bossez pour une grosse multinationale qui pèse des milliards, je vous conseille de voir au-delà de cet article.

Dernière petite précision, je pars du principe que vous utilisez Express.

Sois dans les temps

La première chose dont vous devez faire attention, c’est que votre application Node / Express ne soit pas dans une version obsolète. Ça parait bête, mais sachez que si votre version date de mathusalem, il y a de fortes chances pour que les failles de sécurité connues ne soient plus corrigées par l’équipe de développement Node. Donc c’est à vous de voir si c’est intéressant de passer à la version supérieure ou pas.

Pour voir la version node qui est installé dans votre machine, il vous suffit de taper

$ node -v

N’hésitez pas à vérifier sur le site officiel les recommandations en ce qui concerne votre version. Personnellement, je ne vous conseille pas de prendre la dernière version mais celle qui est recommandé pour la plupart des gens. Et n’oubliez pas de tout tester pour voir si rien n’a été cassé dans l’upgrade version.

Helmet

Ce module très célèbre est en fait un regroupement de plusieurs petits modules qui permettront de sécuriser votre HTTP headers. Pour l’installer, il faut faire

$ npm i helmet

Et pour l’utiliser

const express = require("express");
const helmet = require("helmet");
 
const app = express();
 
app.use(helmet());

Maintenant, vous avez pas mal de défense en ce qui concerne les informations présentes dans le Headers. Vous pouvez aussi faire une configuration plus précise pour Helmet, mais je vous conseille de savoir exactement ce que vous faites.

XSS

Avant de vous dire comment vous protéger contre les attaques XSS, il serait sage que je vous explique ce que c’est. Une attaque XSS, c’est l’injection d’un script dans votre serveur, ce qui modifiera son comportement. Si par exemple dans un formulaire, une personne envoie une longue ligne de code à la place de son prénom, le serveur peut croire que c’est une ligne de code à lancer. Et à ce moment-là, c’est open bar pour les script externes.

Mais pas de panique, ces attaques sont connues dans le monde du dev et plusieurs solutions existent déjà. La plus simple pour votre application node est d’installer un module qui va gérer pour vous. Un des plus connu, c’est XSS.

$ npm install xss
const xss = require("xss");
const html = xss('<script>alert("xss");</script>');
console.log(html);

express-rate-limit

Si une personne veut faire tomber un serveur, il peut envoyer des requêtes en boucle. Ce qui aura comme conséquence de faire stresser votre application Node. Un moyen pour empêcher ce genre de pratique est de limiter le nombre de requête que peut faire un client. Le module que j’utilise personnellement est express-rate-limit

$ npm install --save express-rate-limit
const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
  windowMs: 10 * 60 * 1000, // Voici l’équivalent de 10 minutes
  max: 100 // Le client pourra donc faire 100 requêtes toutes les 10 minutes
});
 
//  Cette limite de 100 requêtes toutes les 10 minutes sera effective sur toutes les routes.
app.use(limiter);

Si vous souhaitez que chaque route ait son propre limiteur, vous pouvez faire comme ça

const rateLimit = require("express-rate-limit");

const apiLimiter1 = rateLimit({
  windowMs: 10 * 60 * 1000, 
  max: 100
});
 
app.use("/api/test1", apiLimiter);

const apiLimiter2 = rateLimit({
  windowMs: 25 * 60 * 1000, 
  max: 150
});

app.use("/api/test2", apiLimiter2);

IP

Imaginons que vous avez un serveur Node qui doit envoyer des données à votre seule application Front, ça implique donc que si une autre adresse IP que votre application Front demande des infos, votre Node doit lui refuser l’accès. Pour ça, nous avons le module IP. Très pratique, il permet de récupérer l’adresse IP de la demande et de faire des comparaisons / validations.

$ npm install ip

Afin de faire votre opération, ce module offre pas mal de méthode qui vous permet de faire ce que vous voulez. Voici un petit copier de ce qu’on trouve sur le site github du module :

var ip = require('ip');

ip.address() // my ip address
ip.isEqual('::1', '::0:1'); // true
ip.toBuffer('127.0.0.1') // Buffer([127, 0, 0, 1])
ip.toString(new Buffer([127, 0, 0, 1])) // 127.0.0.1
ip.fromPrefixLen(24) // 255.255.255.0
ip.mask('192.168.1.134', '255.255.255.0') // 192.168.1.0
ip.cidr('192.168.1.134/26') // 192.168.1.128
ip.not('255.255.255.0') // 0.0.0.255
ip.or('192.168.1.134', '0.0.0.255') // 192.168.1.255
ip.isPrivate('127.0.0.1') // true
ip.isV4Format('127.0.0.1'); // true
ip.isV6Format('::ffff:127.0.0.1'); // true

// operate on buffers in-place
var buf = new Buffer(128);
var offset = 64;
ip.toBuffer('127.0.0.1', buf, offset);  // [127, 0, 0, 1] at offset 64
ip.toString(buf, offset, 4);            // '127.0.0.1'

// subnet information
ip.subnet('192.168.1.134', '255.255.255.192')
// { networkAddress: '192.168.1.128',
//   firstAddress: '192.168.1.129',
//   lastAddress: '192.168.1.190',
//   broadcastAddress: '192.168.1.191',
//   subnetMask: '255.255.255.192',
//   subnetMaskLength: 26,
//   numHosts: 62,
//   length: 64,
//   contains: function(addr){...} }
ip.cidrSubnet('192.168.1.134/26')
// Same as previous.

// range checking
ip.cidrSubnet('192.168.1.134/26').contains('192.168.1.190') // true


// ipv4 long conversion
ip.toLong('127.0.0.1'); // 2130706433
ip.fromLong(2130706433); // '127.0.0.1'

Avec ça, vous devrez normalement trouver votre bonheur.

I’m so secure / insecure

Normalement, si vous utilisez bien les outils plus haut, vous devriez avoir une bonne base pour ne pas vous faire attaquer par des hacker sans scrupules qui prennent plaisir à faire tomber les serveurs. Mais n’oubliez jamais que la meilleure protection pour votre serveur, c’est ce que vous faites, pas ce que vous utilisez.