Skip to main content

Vous devriez utiliser les en-têtes HTTP Strict Transport Security (HSTS) dans votre serveur Node.js

Écrit par

Raphael Mun

feature global security

29 décembre 2022

0 minutes de lecture

Pour la plupart des sites Web et des applications, l’utilisation d’en-têtes HTTP liés à la sécurité est devenue une pratique courante. Les sites Web utilisent les en-têtes dans le cadre des requêtes et des réponses HTTP pour transmettre des informations sur une page ou sur des données envoyées via le protocole HTTP. Ils peuvent inclure un en-tête Content-Encoding pour indiquer que le contenu est un fichier zip compressé, ou un en-tête Location pour spécifier une URL de redirection.

Parmi les nombreux en-têtes de sécurité utilisés par les navigateurs Web modernes pour protéger les utilisateurs, l’en-tête HTTP Strict Transport Security (HSTS) est essentiel. L’en-tête HSTS fourni par un site Web indique au navigateur d’utiliser le protocole HTTPS lors de chaque visite ultérieure.

Pourtant, malgré leur utilité, leur facilité de mise en œuvre et leur prise en charge par pratiquement tous les navigateurs, seuls environ 25 % des réponses HTTP sur mobile et 28 % sur ordinateur incluent des en-têtes HSTS.

Examinons en détail les en-têtes HSTS pour découvrir leur impact sur la sécurité Web et comprendre pourquoi les utiliser avec Node.js. Nous verrons ensuite comment activer HSTS dans un serveur Node.js.

Pourquoi activer HSTS ?

Les connexions HTTPS sont sécurisées, car elles chiffrent et déchiffrent les paquets de données transmis à l’aide de certificats SSL/TLS, vérifiables par des autorités de certification (AC) reconnues. Ainsi, seuls l’ordinateur de l’utilisateur et le serveur peuvent lire les paquets de données transmis, quelle que soit la personne qui intercepte le trafic réseau.

Les connexions HTTP classiques ne sont pas chiffrées : toute personne qui accède aux données peut donc les lire. Parmi ces destinataires non autorisés, on peut trouver les serveurs qui acheminent et transmettent les données de l’utilisateur au serveur de destination, ou encore un pirate qui accède au trafic réseau sur un routeur Wi-Fi public.

Voici quelques conclusions du rapport Web Almanac 2022, auquel Snyk a également contribué.

Graphiques du Web Almanac 2022 indiquant que 25 % des requêtes mobiles comportent un en-tête HSTS, ainsi que les taux d’utilisation des directives HSTS sur ordinateur et mobile.

À l’heure actuelle, seulement 34 % des réponses aux requêtes sur mobile et 37 % sur ordinateur incluent la directive includeSubdomain.

La majorité restante est davantage exposée aux attaques de type man-in-the-middle (MITM), qui peuvent entraîner aussi bien un vol de carte bancaire ou d’identité que la compromission des identifiants d’accès à notre application Web Node.js. Il est important de noter que les acteurs malveillants n’ont pas besoin d’intercepter les données au moment où nous saisissons nos identifiants de connexion. Les cookies du navigateur stockent et envoient souvent nos jetons de session ou d’accès, ce qui permet à ces acteurs malveillants de les obtenir par détournement de session.

Comment fonctionne HSTS ?

Lorsque l’en-tête de réponse HTTP contient un en-tête HSTS, les navigateurs Web savent qu’ils doivent toujours établir une connexion HTTPS avec le serveur et redirigent automatiquement les utilisateurs qui se sont d’abord connectés via HTTP. Dès lors, les connexions à l’application ou au site Web restent chiffrées et sécurisées, ce qui permet à l’application d’utiliser des cookies sécurisés et contribue à prévenir les attaques MITM.

L’en-tête HSTS comporte trois paramètres différents. Découvrons-les.

max - age = <expire-time>

Ce paramètre obligatoire indique, en secondes, pendant combien de temps le navigateur doit se souvenir de se connecter au site via HTTPS. Une fois ce délai écoulé, le navigateur chargera normalement le site lors de la prochaine visite. Le délai d’expiration est actualisé dans le navigateur de l’utilisateur chaque fois qu’il détecte l’en-tête HSTS. Le paramètre peut donc rester actif indéfiniment. Certains sites peuvent également désactiver immédiatement HSTS en définissant cette valeur sur 0.

Pour tester votre configuration HSTS, vous pouvez définir une durée courte, par exemple 300 secondes (cinq minutes). Une fois que vous aurez vérifié que votre site fonctionne correctement, vous pourrez la prolonger, par exemple à 31 536 000 secondes (un an).

includeSubDomains

Le paramètre includeSubDomains est une option facultative qui indique au navigateur d’activer HSTS pour le site Web et tous ses sous-domaines. Comme ce paramètre s’applique largement, vérifiez que tous vos sous-domaines prennent en charge HTTPS avant de l’activer.

preload

Une première connexion pourrait utiliser une connexion HTTP compromise. Pour y remédier, Google gère le paramètre non officiel preload. La plupart des principaux navigateurs prennent en charge cet en-tête de sécurité, qui réduit considérablement le risque. Un site Web peut ajouter le paramètre preload à l’en-tête HSTS, puis enregistrer son domaine auprès du service preload. Les navigateurs peuvent ensuite consulter cette liste de référence pour déterminer si leur première connexion peut utiliser HTTPS. Notez que le paramètre preload ne fonctionne que si vous définissez max-age sur au moins 31 536 000 secondes et activez le paramètre includeSubDomains.

Notez qu’une fois votre domaine ajouté au service preload, le traitement d’une demande de suppression peut prendre plusieurs mois. Si vous souhaitez enregistrer votre domaine pour le préchargement, prenez connaissance des conditions de soumission et des recommandations de déploiement.

Renforcer la sécurité avec HSTS

Voyons comment configurer un serveur Node.js pour qu’il renvoie un en-tête HSTS. À noter : tous les exemples de code utilisés dans cet article sont publics et disponibles dans le dépôt open source sur GitHub.

Prérequis

Ce projet nécessite les éléments suivants pour fonctionner :

  • Un nom de domaine enregistré

  • Un serveur équipé de Node.js, avec les ports réseau 80 et 433 disponibles

  • Un enregistrement DNS valide pour le domaine, pointant vers le serveur

Obtenir un certificat SSL/TLS

Avant d’activer HTTPS, nous avons besoin d’un certificat et d’une clé privée pour le domaine. Si vous n’en avez pas encore, vous pouvez en obtenir un auprès de Let’s Encrypt en suivant les étapes ci-dessous :

Commencez par installer certbot en suivant ces instructions. Sélectionnez autre dans le menu déroulant Logiciel, puis choisissez le système d’exploitation de votre serveur dans le menu déroulant Système. Suivez ensuite les instructions qui s’affichent.

Page Web contenant les instructions de Certbot et affichant les options sélectionnées pour un site HTTP fonctionnant avec « Other » et CentOS 8.

Ensuite, vérifiez que les ports 80 et 443 sont ouverts sur le serveur, puis exécutez la commande suivante en indiquant le domaine de votre serveur :

certbot certonly --standalone -d your.domain.url

Vous devrez peut-être précéder la commande de sudo et l’exécuter avec des privilèges élevés.

Sortie du terminal montrant que Certbot a obtenu et enregistré avec succès un certificat HTTPS Let's Encrypt pour snykdemo.instafluff.tv.

Exécutez maintenant certbot certificates (avec sudo si nécessaire) pour obtenir les chemins d’accès au certificat et à la clé privée.

Terminal affichant la sortie de Certbot pour le certificat de snykdemo.instafluff.tv, notamment sa date d’expiration et les chemins du certificat et de la clé privée Let’s Encrypt.

Configurer le code du serveur

Commencez par créer un dossier de projet sur le serveur en exécutant la commande : mkdir snyk-demo.

Ensuite, accédez au dossier avec la commande : cd snyk-demo.

Initialisez ensuite le dossier en tant que projet Node.js avec la commande : npm init -y.

Terminal affichant la commande npm init qui crée un fichier package.json pour le projet snyk-demo sous Node.js.

Installons maintenant les dépendances du projet pour le serveur. Suivez les instructions correspondant à Express ou Fastify.

Express :

npm install express helmet

Fastify :

npm install fastify @fastify/helmet

Nous pouvons maintenant créer un serveur Node.js Express de base, qui gère HTTP et HTTPS.

Créez un fichier index.js dans ce dossier, puis ajoutez les modules Node suivants au début du code.

Express :

const fs = require( "fs" );
const http = require( "http" );
const https = require( "https" );
const express = require( "express" );
const helmet = require( "helmet" );

Fastify :

const fs = require( "fs" );
const helmet = require( "@fastify/helmet" );

Ajoutez le code suivant pour charger les fichiers de certificat et remplacez la valeur de certificatePath par le chemin d’accès au certificat de votre domaine. Si vous avez utilisé certbot de LetsEncrypt, vous pouvez exécuter certbot certificates pour récupérer les chemins d’accès aux fichiers.

Express :

// Replace the following with your domain certificate path
const certificatePath = `/etc/letsencrypt/live/snykdemo.instafluff.tv/;
const privateKey = fs.readFileSync( `${certificatePath}/privkey.pem, "utf8" );
const certificate = fs.readFileSync( `${certificatePath}/fullchain.pem, "utf8" );
// Add the following if your certificate includes a certificate chain
// const ca = fs.readFileSync( `${certificatePath}/chain.pem, "utf8" );

const credentials = {
    key: privateKey,
    cert: certificate,
    // ca: ca, // Add the ca if there is a certificate chain
};

Fastify :

// Replace the following with your own domain certificate path
const certificatePath = `/etc/letsencrypt/live/snykdemo.instafluff.tv/`;
const fastify = require( "fastify" )({
    https: {
        key: fs.readFileSync( `${certificatePath}/privkey.pem, "utf8" ),
        cert: fs.readFileSync( `${certificatePath}/fullchain.pem, "utf8" ),
        // // Add the following if your certificate includes a certificate chain
        // ca: fs.readFileSync( `${certificatePath}/chain.pem, "utf8" ),
    },
});
const fastifyHttp = require( "fastify" )();

Il est maintenant temps d’initialiser l’application Express/Fastify et de créer une route de base.

Le code suivant illustre les trois paramètres HSTS, mais vous pouvez commencer par utiliser uniquement le paramètre max-age.

Option 1 : utiliser le middleware Helmet

La première méthode pour configurer HSTS consiste à utiliser le middleware Helmet lors de l’initialisation de l’application :

Express :

const app = express();
app.use( helmet() );
app.use( helmet.hsts( { maxAge: 300, includeSubDomains: true, preload: true } ) );

app.use( ( req, res ) => {
res.send( "Hello secure web!" );
});

Fastify :

const hsts = { maxAge: 300, includeSubDomains: true, preload: true };
fastify.register( helmet, { hsts } );
fastifyHttp.register( helmet, { hsts } );

Option 2 : définir manuellement l’en-tête

Vous pouvez également définir manuellement l’en-tête HSTS au moyen d’un middleware qui l’applique à chaque réponse :

Express :

const app = express();

app.use( ( req, res ) => {
    if( req.secure ) {
         res.setHeader( "Strict-Transport-Security", "max-age=300; includeSubDomains; preload" );
    }
    res.send( "Hello secure web!" );
});

Fastify :

fastify.get( "/", ( req, res ) => {
    res.header( "Strict-Transport-Security", "max-age=300; includeSubDomains; preload" );
    res.send( "Hello secure web!" );
});
fastifyHttp.get( "/", ( req, res ) => {
    res.header( "Strict-Transport-Security", "max-age=300; includeSubDomains; preload" );
    res.send( "Redirect to HTTPS" );
});

Pour terminer le code, créons les serveurs HTTP et HTTPS avec le code suivant :

Express :

const httpServer = http.createServer( app );
httpServer.listen( 80, () => {
console.log( "HTTP server started." );
});

const httpsServer = https.createServer( credentials, app );
httpsServer.listen( 443, () => {
console.log( "HTTPS server started" );
});

Fastify :

fastifyHttp.listen( { port: 80, host: "0.0.0.0" }, ( err, address ) => {
    console.log( "HTTP server started" );
});

fastify.listen( { port: 443, host: "0.0.0.0" }, ( err, address ) => {
    console.log( "HTTPS server started" );
});

Pour référence, voici les liens vers les dépôts de code des projets Express et Fastify.

Enfin, lancez le code du serveur avec node index.js, puis ouvrez votre domaine dans un navigateur Web.

bitnami@ip-172-26-14-29:~/snyk-demo$ sudo node index.js
HTTP server started.
HTTPS server started

Ouvrez les outils de développement de votre navigateur et examinez les en-têtes de réponse dans l’onglet Network. Vous verrez que l’en-tête HSTS est bien spécifié.

En-têtes réseau de Chrome DevTools affichant l’en-tête de réponse Strict-Transport-Security avec max-age=300, includeSubDomains et preload

Et ensuite ?

Vous savez maintenant pourquoi les en-têtes HTTP Strict Transport Security sont importants pour la sécurité des applications Node.js, comment ils contribuent à prévenir les attaques MITM et comment les mettre en œuvre dans votre code Node.js. Vous disposez donc des outils nécessaires pour sécuriser les applications Web de vos utilisateurs.

Il vous reste un point essentiel à retenir. En environnement de production, vous ne devez pas configurer HSTS ni servir SSL depuis votre application Node.js. Vous devez plutôt confier ces tâches à des serveurs HTTP de fichiers statiques tels que NGINX ou Apache.

Lire la suite

blog feature snyk container custom base recommendations
Blog

Découvrez Evo Agentic AppSec : une sécurité au rythme du développement de l’IA

Le développement piloté par l’IA dépasse les workflows de sécurité traditionnels. L’AppSec agentique de Snyk s’appuie sur des agents de sécurité pour détecter, corriger et vérifier les vulnérabilités en continu, tandis que les équipes définissent les exigences.

blog feature ai violet
Blog

VulnBump : les agents IA peuvent-ils corriger les dépendances vulnérables sans casser le build ?

VulnBump évalue des agents IA sur 307 mises à niveau de dépendances réelles. Une correction n’est comptabilisée que si la vulnérabilité disparaît et que la compilation et les tests restent au vert.

Blog

Les modèles de pointe ont trouvé les vulnérabilités. Seul l’attaquant a trouvé les chaînes d’exploitation.

L’analyse statique a détecté les failles, mais seuls des tests d’attaque en conditions réelles ont prouvé comment elles pouvaient être enchaînées pour provoquer des compromissions. Comparaison d’Evo COS, de Claude Security et de Claude Code Security.