Skip to main content

5 exemples de code pour sécuriser Node.js que tout développeur backend devrait connaître

Écrit par
feature nodejs security snippets

28 février 2024

0 minutes de lecture

En tant que développeurs backend, nous avons la responsabilité essentielle de garantir la sécurité de nos applications. Node.js n’échappe pas à cette responsabilité et sa popularité croissante en fait une cible de choix pour les pirates. Il est donc impératif de suivre les bonnes pratiques de sécurité lorsqu’on travaille avec Node.js.

Dans cet article, nous allons découvrir quelques exemples de code essentiels pour sécuriser Node.js que tout développeur backend devrait connaître en 2024. Les vulnérabilités des logiciels Node.js peuvent représenter une menace importante pour toute application. Elles peuvent entraîner des accès non autorisés, des fuites de données et, dans le pire des cas, une compromission complète du système. C’est pourquoi le respect des bonnes pratiques de sécurité n’est pas simplement recommandé : il est indispensable.

Dans le contexte de Node.js, cela signifie veiller à ce que votre code ne présente aucune faille exploitable. Cela consiste notamment à assainir les entrées utilisateur pour prévenir les attaques par injection, à traiter correctement les mots de passe comme des données sensibles et à gérer les dépendances pour éviter les vulnérabilités de tiers.

Nous allons passer en revue les concepts de sécurité Node.js suivants et les exemples de code associés, sélectionnés pour leur efficacité à prévenir les vulnérabilités courantes et leur accessibilité aux développeurs, sans expertise supplémentaire en sécurité :

  1. Utiliser le modèle d’autorisations de Node.js pour restreindre l’accès aux ressources lors de l’exécution

  2. Valider les entrées à l’aide d’un schéma JSON Fastify

  3. Sécuriser le hachage des mots de passe avec Bcrypt

  4. Prévenir les attaques par injection SQL avec Knex.js

  5. Mettre en place une limitation du débit avec fastify-rate-limit

1. Utiliser le modèle d’autorisations de Node.js pour restreindre l’accès aux ressources lors de l’exécution

Le modèle d’autorisations de Node.js peut jouer un rôle essentiel dans la sécurisation de vos applications. Élément clé du modèle de sécurité de base de Node.js, il contribue à protéger vos applications contre les attaques et les activités malveillantes. À l’instar des garanties de sécurité offertes par les restrictions de ressources centrées sur les processus de Deno, Node.js dispose désormais, dans une certaine mesure, d’un modèle d’autorisations similaire.

Imaginons qu’une application Node.js doive convertir des fichiers PDF en images PNG. Nous pouvons utiliser le package npm pdf-image pour effectuer cette tâche. Le package pdf-image utilise des processus enfants pour réaliser la conversion. Pour cela, nous devons utiliser l’option --allow-child-process dans l’environnement d’exécution Node.js (fournie par le modèle d’autorisations).

Voici un exemple de code qui l’illustre :

const { PDFImage } = require('pdf-image');
const path = require('path');

const pdfPath = path.resolve(__dirname, 'sample.pdf');
const pdfImage = new PDFImage(pdfPath, {
  convertOptions: {
    '-density': '300',
    '-quality': '80'
  },
  combinedImage: true
});
pdfImage.convertFile().then(() => {
  console.log('PDF converted to PNG successfully');
}).catch((err) => {
  console.error(`Failed to convert PDF to PNG: ${err}`);
});

Dans cet exemple, nous créons une nouvelle instance de PDFImage en lui indiquant le chemin d’accès à notre fichier PDF et quelques options de conversion. Nous appelons ensuite convertFile() pour convertir le PDF en image PNG.

Si vous activez le modèle d’autorisations expérimental de Node.js, vous devrez lancer explicitement l’environnement d’exécution Node.js avec l’option de ligne de commande --allow-child-process mentionnée précédemment, car la bibliothèque pdf-image lance un processus enfant pour convertir les fichiers PDF.

Si le modèle d’autorisations de Node.js offre un excellent moyen de restreindre l’accès aux ressources système, il est également essentiel de connaître les vulnérabilités de sécurité potentielles des packages que nous utilisons. C’est là qu’intervient l’extension Snyk pour Visual Studio Code. Elle peut détecter le code non sécurisé et les dépendances vulnérables dans une application Node.js. Par exemple, le package pdf-image mentionné précédemment présente une vulnérabilité connue d’injection de commandes, liée à son utilisation de processus enfants. Cette vulnérabilité est connue depuis 2018 et, malheureusement, aucun correctif n’est disponible à ce jour.

Cela montre combien il est important d’utiliser des outils comme Snyk pour rester informé des vulnérabilités de sécurité présentes dans les packages dont nous dépendons. C’est aussi un rappel de la prudence dont nous devons faire preuve avec certaines fonctionnalités, comme les processus enfants, qui peuvent présenter des risques de sécurité si elles ne sont pas utilisées correctement.

En tant que développeurs Node.js, nous avons la responsabilité de veiller à ce que le code que nous écrivons soit non seulement fonctionnel, mais aussi sécurisé. Pour renforcer la sécurité d’une application Node.js, il est notamment essentiel d’utiliser le modèle d’autorisations intégré afin de restreindre l’accès aux ressources système lors de l’exécution. Si votre application Node.js n’a pas besoin de créer des processus enfants, n’activez pas cette ressource afin de ne pas élargir la surface d’attaque.

Sécurisez vos applications JavaScript dès maintenant

Détectez et corrigez gratuitement les vulnérabilités JavaScript avec Snyk. 

Aucune carte de crédit requise.

Ou inscrivez-vous avec Azure AD Docker ID Bitbucket

En utilisant Snyk, vous acceptez de respecter nos politiques, notamment nos Conditions d’utilisation et notre Politique de confidentialité.

FAQ sur le modèle d’autorisations de Node.js

Quelles menaces de sécurité le modèle d’autorisations de Node.js permet-il de prévenir ?

Le modèle d’autorisations de Node.js est conçu pour prévenir diverses menaces de sécurité, notamment l’accès non autorisé aux fichiers, les attaques par injection de commandes et l’élévation de privilèges.

Puis-je personnaliser le modèle d’autorisations de Node.js ?

Oui, certaines ressources régies par le modèle d’autorisations de Node.js sont hautement personnalisables. Par exemple, lorsque vous limitez l’accès aux ressources de fichiers, vous pouvez spécifier différents fichiers ou chemins de fichiers, comme --allow-fs-read=*.

Le modèle de permissions de Node.js suffit-il à sécuriser mes applications ?

Le modèle de permissions de Node.js est un élément essentiel de la sécurité des applications, mais il ne suffit pas à lui seul. Vous devez également adopter des pratiques de codage sécurisées, auditer régulièrement votre code et vos dépendances tierces pour repérer les vulnérabilités, et utiliser des outils comme Snyk pour détecter et corriger les problèmes de sécurité potentiels.

2. Valider les entrées à l’aide d’un schéma JSON Fastify

La validation des entrées est un aspect essentiel du développement backend. Si vous avez déjà créé des API avec Express, Fastify ou d’autres frameworks, vous en avez probablement déjà compris l’importance. Cette pratique de sécurité garantit que seules les données correctement formatées entrent dans votre système et permet ainsi de prévenir les risques potentiels. Dans vos applications Node.js, vous pouvez notamment valider les entrées en utilisant le schéma Fastify pour vos applications web Fastify.

L’approche de Fastify fondée sur les schémas

Fastify utilise une approche fondée sur les schémas pour valider les entrées. Bien que cela ne soit pas obligatoire, il est recommandé d’utiliser le schéma JSON pour valider vos routes et sérialiser vos sorties. Le schéma JSON définit un contrat pour vos données en précisant le format attendu, les types de données, les champs obligatoires et d’autres contraintes. Vous pouvez considérer le schéma JSON d’une route Fastify comme l’équivalent de l’utilisation de TypeScript pour garantir un typage fort dans votre code lors de la compilation.

Prenons l’exemple d’une application Fastify simple qui crée un utilisateur. Nous pouvons utiliser un schéma Fastify pour valider les données saisies.

const fastify = require('fastify')({ logger: true });

const UserSchema = {
  body: {
    type: 'object',
    properties: {
      name: { type: 'string' },
      email: { type: 'string', format: 'email' },
      password: { type: 'string', minLength: 8 }
    },
    required: ['name', 'email', 'password']
  }
};

// User creation route
fastify.post('/users', { schema: UserSchema }, async (request, reply) => {
  // Example user creation logic
  // In a real application, you would replace this with actual database logic
  const user = request.body;

  // Simulating user creation
  console.log("Creating user:", user);

  // Responding with the created user (in real applications, never send the password back)
  return reply
    .code(201)
    .send({ success: true, message: "User created", user: { name: user.name, email: user.email } });
});

// Server startup
const start = async () => {
  try {
    await fastify.listen({ port: 6000, host: 'localhost' });
    console.log(`Server running at http://localhost:3000/`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

Dans le code ci-dessus, nous définissons un UserSchema qui exige un nom, une adresse e-mail et un mot de passe. L’adresse e-mail doit être au format valide et le mot de passe doit comporter au moins 8 caractères.

Négliger la validation des entrées peut exposer votre application à divers risques de sécurité, notamment la falsification de requêtes côté serveur (SSRF) ou les attaques par pollution des paramètres HTTP. Les attaques SSRF peuvent inciter le serveur à envoyer des requêtes non autorisées, ce qui risque d’exposer des données. La pollution des paramètres HTTP peut quant à elle manipuler ou corrompre des requêtes et entraîner un comportement inattendu.

Des outils comme Snyk peuvent vous aider à détecter ces vulnérabilités en analysant votre code à la recherche de failles de sécurité. Ils peuvent également proposer des conseils de correction pour vous aider à les résoudre et ainsi renforcer la sécurité globale de votre application.

FAQ sur la validation des entrées avec Fastify

La validation des entrées est-elle nécessaire ?

Oui, la validation des entrées est une pratique de sécurité fondamentale qui empêche les données mal formatées d’entrer dans votre système.

Puis-je utiliser d’autres bibliothèques de validation avec Fastify ?

Oui, Fastify est flexible et vous permet d’utiliser d’autres bibliothèques de validation, comme Joi, yup ou ajv.

La validation des schémas Fastify a-t-elle un impact sur les performances ?

Fastify est conçu pour offrir des performances élevées, et la validation des schémas a un impact minime sur les performances.

4. Qu’est-ce qu’un schéma JSON Fastify ?

Les schémas Fastify servent à valider les données des requêtes et des réponses dans vos routes, afin de garantir que seules les données conformes aux critères spécifiés sont traitées. Vous réduisez ainsi le risque de traiter des données non valides susceptibles d’entraîner des failles de sécurité ou des erreurs dans l’application.

3. Sécuriser le hachage des mots de passe avec Bcrypt

Il est essentiel de stocker les mots de passe des utilisateurs de manière sécurisée. En cas de fuite de données, vous devez vous assurer que les mots de passe des utilisateurs ne puissent pas être facilement déchiffrés. C’est là qu’intervient le hachage des mots de passe : il s’agit de convertir une clé donnée en une autre valeur. Une fonction de hachage génère cette nouvelle valeur et doit idéalement produire un résultat unique (ou code de hachage) pour chaque valeur d’entrée unique.

Si vous choisissez de mettre en œuvre vous-même l’authentification au lieu de vous appuyer sur un service ou une autre bibliothèque, il est essentiel de comprendre que la façon dont vous gérez la sécurité des mots de passe peut avoir une incidence directe sur la sécurité globale de votre application.

Présentation de Bcrypt pour le hachage des mots de passe dans Node.js

Bcrypt est un algorithme et un package npm qui fournit une fonction de hachage des mots de passe considérée comme très sûre. Il intègre un salt (données aléatoires) pour protéger contre les attaques par tables arc-en-ciel et permet de configurer un facteur de travail, qui détermine l’intensité de calcul du hachage — une fonctionnalité utile pour prévenir les attaques par force brute.

Voyons un exemple d’utilisation simple :

const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 'my_password';

// Define an async function
async function hashPassword(plaintextPassword) {
  try {
    const hash = await bcrypt.hash(plaintextPassword, saltRounds);
    // Store hash in your password DB.
    console.log(hash); // Example of how to use the hash
  } catch (err) {
    // Handle error
    console.error(err);
  }
}

// Call the async function
hashPassword(myPlaintextPassword);

Le paramètre saltRounds détermine la complexité du processus de hachage. Plus sa valeur est élevée, plus le processus est long, ce qui peut contribuer à protéger contre les attaques par force brute. La fonction hash ajoute automatiquement un salt au mot de passe en clair, puis le hache. Le hachage obtenu peut ensuite être stocké dans votre base de données.

FAQ sur le hachage des mots de passe et la gestion de l’authentification dans Node.js

Quelle longueur de sel est recommandée avec Bcrypt ?

Bcrypt génère automatiquement un sel de 16 octets.

À quelle fréquence dois-je mettre à jour mon algorithme ou ma stratégie de hachage ?

En cas d’avancée majeure dans les technologies de hachage ou de découverte d’une vulnérabilité dans votre algorithme actuel, il est recommandé de mettre à jour votre stratégie de hachage. Node.js prend en charge nativement l’algorithme scrypt dans le module principal node:crypto.

Qu’est-ce que le facteur de coût de Bcrypt ?

Le facteur de coût détermine l’intensité des calculs requis pour le processus de hachage. Plus il est élevé, plus le hachage est sécurisé, mais plus son calcul prend de temps. Avec Node.js, cela peut aussi avoir un impact direct sur la boucle d’événements et la réactivité de votre application, selon la façon dont vous générez le hachage.

4. Prévenir les attaques par injection SQL avec Knex.js

L’injection SQL est une vulnérabilité de sécurité courante qui représente un risque important pour les données des applications. Les développeurs Node.js peuvent atténuer ce risque en utilisant Knex.js, un générateur de requêtes SQL prometteur, pour créer des requêtes plus sûres. Notez toutefois que Knex.js permet également de construire et d’exécuter des requêtes SQL brutes. Dans ce cas, l’injection SQL reste un risque de sécurité : elle peut être introduite dans la requête si des entrées utilisateur y sont concaténées.

Knex.js est un puissant générateur de requêtes SQL pour Node.js. Il prend en charge les transactions, le regroupement des connexions, les migrations et les seeds, ce qui en fait un choix de prédilection pour les développeurs qui souhaitent écrire des requêtes SQL sécurisées, robustes et évolutives. Knex.js protège contre les attaques par injection SQL en utilisant des requêtes paramétrées et en échappant les valeurs insérées dans les instructions SQL.

Voici un exemple de code qui montre comment Knex.js protège contre les attaques par injection SQL :

const knex = require('knex')({
  client: 'pg',
  connection: {
    host : '127.0.0.1',
    user : 'your_database_user',
    password : 'your_database_password',
    database : 'myapp_test'
  }
});

// This value is provided by the user and could be malicious
// such as applying an OR 1=1 SQL injection or other
// techniques that escape the original context of the query
// and create a new one
let userProvidedValue = 'maliciousValue';

knex('users')
  .where('id', '=', userProvidedValue)
  .select()
  .then(rows => {
    // process result
  })
  .catch(err => {
    // handle error
  });

Dans cet exemple, Knex.js échappe automatiquement la valeur userProvidedValue et prévient ainsi toute attaque potentielle par injection SQL.

Comme nous l’avons indiqué, Knex.js ne constitue pas à lui seul un environnement isolé ni une protection complète contre les injections SQL. Voici un exemple d’utilisation potentiellement non sécurisée de Knex.js, susceptible d’entraîner une attaque par injection SQL :

knex.raw(`SELECT * FROM users WHERE id = ${userProvidedValue}`)

Dans ce cas, la valeur userProvidedValue n’est ni échappée ni paramétrée. La requête est donc vulnérable à une injection SQL si userProvidedValue contient du code SQL malveillant. Snyk peut aider les développeurs à détecter ce type de vulnérabilité dans leur base de code. Snyk analyse votre code et fournit des recommandations concrètes pour corriger les vulnérabilités de sécurité, notamment les attaques potentielles par injection SQL.

Détectez et corrigez les vulnérabilités JavaScript

Sécurisez vos applications grâce à l’analyse des vulnérabilités de Snyk et à ses recommandations de correction.

Aucune carte bancaire requise.

Ou inscrivez-vous avec Azure AD Docker ID Bitbucket

En utilisant Snyk, vous acceptez de respecter nos politiques, notamment nos Conditions d’utilisation et notre Politique de confidentialité.

FAQ sur la prévention des injections SQL avec Knex.js

Qu’est-ce qu’une injection SQL ?

Une injection SQL est une technique qui consiste, pour un attaquant, à insérer du code SQL malveillant dans une requête. Cette intrusion peut entraîner un accès non autorisé à des données sensibles, leur altération, voire leur perte. Il est donc essentiel de prévenir les attaques par injection SQL pour garantir la sécurité des applications.

Knex.js est-il à l’abri des injections SQL ?

Bien que Knex.js atténue efficacement les risques d’injection SQL grâce aux requêtes paramétrées et à l’échappement des valeurs, il n’est pas totalement à l’abri. Les développeurs doivent veiller à utiliser Knex.js correctement et à ne pas introduire de vulnérabilités.

Knex.js suffit-il à prévenir les injections SQL ?

Knex.js est un outil puissant pour prévenir les injections SQL, mais il ne suffit pas à lui seul. Les développeurs doivent également adopter d’autres bonnes pratiques de sécurité, comme la validation des entrées et le principe du moindre privilège.

5. Mettre en place une limitation du débit avec fastify-rate-limit

La limitation du débit est un mécanisme de sécurité essentiel qui protège vos applications web contre les attaques par déni de service (DoS). En contrôlant le nombre de requêtes qu’un client peut envoyer à votre application pendant une période donnée, vous empêchez les clients malveillants de submerger votre serveur de requêtes et garantissez ainsi la disponibilité de votre application pour les autres utilisateurs légitimes.

fastify-rate-limit est un plugin pour le framework web Fastify qui facilite la mise en place d’une limitation du débit dans vos applications Node.js. Il vous permet de définir le nombre maximal de requêtes qu’un client peut envoyer pendant une période donnée, ainsi que la réponse à renvoyer lorsque cette limite est dépassée.

Voici un exemple simple de mise en place d’une limitation du débit dans votre application Node.js avec fastify-rate-limit :

const fastify = require('fastify')()

fastify.register(require('@fastify/rate-limit'), {
  // max number of connections during windowMs milliseconds before sending a 429 response
  max: 100,
  timeWindow: '1 minute'
})

fastify.get('/', (req, reply) => {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, err => {
  if (err) throw err
  console.log('Server listening at http://localhost:3000')
})

Dans cet exemple de code, le package de limitation du débit de Fastify est configuré pour limiter chaque client à 100 requêtes par minute. Si un client dépasse cette limite, le serveur renvoie le code d’état 429 (« Too Many Requests »).

FAQ sur la limitation du débit

La limitation du débit peut-elle affecter les performances de mon application ?

Non, la limitation du débit améliore les performances et la disponibilité de votre application en l’empêchant d’être submergée par un grand nombre de requêtes.

Que faire si un utilisateur légitime dépasse la limite de débit ?

Selon les besoins de votre application, vous pouvez augmenter la limite de débit, exclure certaines adresses IP de la limitation de débit ou mettre en place une stratégie plus élaborée qui tient compte du comportement et de la réputation de l’utilisateur.

La limitation du débit peut-elle empêcher tous les types d’attaques par déni de service (DoS) ?

La limitation du débit est efficace contre les attaques par déni de service qui visent à submerger votre serveur avec un grand nombre de requêtes. Elle ne peut toutefois pas vous protéger contre les autres types d’attaques par déni de service qui exploitent des vulnérabilités spécifiques de votre application ou de votre serveur. Il est donc important de mettre en place une stratégie de sécurité complète, qui inclut la limitation du débit ainsi que d’autres mesures de sécurité.

Pourquoi les développeurs devraient utiliser Snyk pour sécuriser JavaScript

Dans cet article, nous avons présenté cinq exemples de code essentiels pour sécuriser Node.js que tout développeur backend devrait connaître. Nous avons souligné l’importance des pratiques de programmation sécurisée, comme la prévention des injections SQL, et leur rôle majeur dans le renforcement de la sécurité des applications. Avec l’évolution de la technologie et la complexité croissante des cybermenaces, il est impossible de trop insister sur la nécessité d’écrire du code sécurisé.

En tant que développeur, il est essentiel de tirer parti d’outils de sécurité robustes capables de détecter et d’atténuer les vulnérabilités potentielles de votre code. Snyk est l’un de ces outils : cette puissante plateforme de sécurité pour les développeurs leur permet de détecter et de corriger les vulnérabilités dans le code, les dépendances, les conteneurs et bien plus encore. Avec Snyk, vous pouvez surveiller en continu les vulnérabilités de sécurité de votre application et recevoir automatiquement des demandes de fusion proposant des correctifs dès qu’une nouvelle vulnérabilité est découverte.

// Install Snyk CLI
npm install -g snyk

// Then run Snyk to find vulnerabilities
snyk test

Snyk s’intègre parfaitement à vos environnements de développement intégrés et à votre pipeline CI/CD, pour vous permettre d’assurer une sécurité continue tout au long du cycle de développement de vos applications. Il prend en charge de nombreux langages de programmation, dont Node.js, et constitue ainsi un outil polyvalent pour les développeurs, quelle que soit leur plateforme.

Écrire du code sécurisé est une pratique fondamentale du développement logiciel que tout développeur backend devrait privilégier. L’utilisation d’outils de sécurité comme Snyk renforce cette pratique grâce à la détection automatisée des vulnérabilités et à leur correction. Face à l’évolution constante des cybermenaces, ces pratiques et ces outils seront essentiels pour préserver la sécurité et l’intégrité de nos applications.

Snyk pour sécuriser JavaScript

De votre première ligne de code à votre dernière dépendance npm, Snyk sécurise vos applications JavaScript directement depuis votre IDE, votre CLI et vos workflows Git.

Lire la suite

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.

feature insights context
Blog

Les attaques autonomes sont déjà là. La défense doit suivre leur rythme.

Les attaquants autonomes réduisent la fenêtre de défense. Découvrez comment la découverte, la correction, la validation et la prévention continues peuvent aider les équipes de sécurité à suivre le rythme.

Blog

Pourquoi les agents de codage IA créent-ils sans cesse des failles de contrôle d’accès ?

Les agents de codage IA peuvent générer une logique d’autorisation qui compile et passe la revue, tout en exposant les données d’un tenant à un autre. Découvrez pourquoi les failles de contrôle d’accès sont difficiles à détecter et comment les prévenir.