Comment sécuriser son application Javascript en 2022 ?

“La gestion des dépendances est un des problèmes les plus critiques dans le développement logiciel”. Michael Feathers

No items found.
October 2, 2022

JavaScript, langage multi-paradigmes (objet, scripting, prototype, fonctionnel) connaît un succès populaire, notamment dans le développement web. À l’origine, c’est un langage de scripting léger. Certains de ses détracteurs m’ont interpellée avec véhémence : JavaScript pose de sérieux problèmes de sécurité. Certaines fonctionnalités natives sont dangereuses, l'exécution côté serveur peut compromettre tout le système.

Le propos de cet article est d’exposer des moyens de sécurisation utilisables pendant tout le cycle de vie d’une application full-stack JavaScript.

Concepts-clés

  • SAST (Static Analysis Security Testing) : analyse statique du code source pour y détecter des patterns de vulnérabilité. Cela peut provoquer de nombreux faux positifs.
  • DAST (Dynamic Analysis Security Testing) : exécution de tests d’intrusion automatisés, dans une sandbox. Au runtime, scanne des réponses à une variété de payloads pour trouver des défauts de sécurité.
  • IAST (Interactive Application Security Testing) : technique d’analyse mélangeant les SAST et DAST. Elle a l’avantage d’analyser des routes de codes réelles (exécutées en production ou pendant les tests). Ce qui limite les faux positifs. Elle n’est pas aussi répandue que les deux autres. 
  • AST (Abstract Syntax Tree) : Arbre de la Syntaxe Abstraite construit après l’analyse du code. C’est la base utilisée par le linter pour trouver les erreurs. Wiki
  • CSP (Content Security Policy) : Politique de Sécurité de Contenu
  • Forensic Analysis : Science forensique : La criminalistique est l'ensemble des techniques mises en œuvre par la justice, la police et la gendarmerie pour établir la preuve d'un délit ou d'un crime et en identifier son auteur. What is forensic analysis, Forensic analysis and the security of infrastructure 
  • Surface d'attaque : Somme de tous les vecteurs d’attaque qu’une personne peut utiliser pour s’introduire dans une application ou un système. Wiki 
  • Attaquant.e : individu ou groupe d’individus, généralement mal intentionné(s) qui recherche(nt) et tente(nt) d’exploiter les failles de sécurité.
  • Âge de la vulnérabilité : durée de vie depuis l’introduction de la vulnérabilité dans le code. Plus la vulnérabilité est “âgée”, plus grand est le risque d’exploitation.
  • Déni de service : Dos (Denial of Service), attaque consistant à faire planter une application, que ce soit via la pollution de prototype ou encore une regex malicieuse. Une intervention manuelle est indispensable pour y remédier. L’application est donc indisponible pendant plus ou moins longtemps.

Les dépendances

En 2019, le State of the Software Supply Chain de Sonatype constatait que plus de 80% du code des solutions modernes provient des dépendances. D’après l’édition 2020, près de 40% des paquets NPM reposent sur du code contenant des vulnérabilités connues.

Les éditions 2017 et 2021 de l’OWASP top 10 remontent le risque de baser son projet sur des dépendances vulnérables.

Le couplage commence avec les dépendances :

“La gestion des dépendances est un des problèmes les plus critiques dans le développement logiciel”. Michael Feathers

Réduire la surface d’attaque en limitant le nombre de dépendances

Le premier enjeu est donc de bien définir les dépendances. Avant chaque nouvel ajout au projet, il est bon de se renseigner sur le module et ses alternatives. Sur NPM trends (www.npmtrends.com), les principales statistiques des packages permettent de faire un choix  plus éclairé.

Dans un souci de stabilité, limiter le nombre de dépendances amoindrit la vulnérabilité de votre solution. Une dépendance embarquant ses dépendances transitives. Chaque dépendance doit être régulièrement mise à jour. Pour en assurer le bon déroulement, figer les versions dans le `package.json` en supprimant les “matchers” comme `^`, `~`, `<` ou `>` et en versionnant le `package-lock.json`. Cela devrait réduire le risque de voir planter le build du projet, au cas où la mise à jour d’une dépendance (ou d’une de ses dépendances transitives) la rendrait incompatible. 

Lockfile-lint passe en revue les fichiers `lock` pour une meilleure sécurité et renforce les politiques de sécurité pour parer l’injection de packages malicieux et autres configurations faillibles (https://github.com/lirantal/lockfile-lint).
`npm audit` ou snyk permet, par exemple, de détecter les librairies qui ne sont plus à jour.
La commande `npm-shrinkwrap` génère un fichier, npm-shrinkwrap.json. Cette pratique est recommandée uniquement pour les applications déployées via le processus de publication dans le registre NPM : comme les daemons, les outils de lignes de commandes installés globalement ou encore les devDependencies.

Attention au typosquatting

Risque propre à tout gestionnaire de paquets avec des modules non vérifiés. Le typosquatting consiste à tirer profit de la notoriété d’un package pour diffuser des librairies malicieuses.

L’incident “crossenv” embarquait les mêmes fonctionnalités que “cross-env” et envoyait les variables d’environnement vers un serveur distant.

(sources: https://snyk.io/blog/typosquatting-attacks, https://adtmag.com/articles/2020/10/01/sonatype-catches-typosquatters.aspx, https://thenewstack.io/npm-cleans-typosquatting-malware)

Le typosquatting existe dans d’autres registres. Que ce soit PyPi ou Rubygems. L’open-source offre des solutions pour toutes les facettes du développement.

La sécurité open-source

NPM Public Advisories centralise les données liées aux failles de sécurité dans l’écosystème JavaScript. Le Node advisories navigator permet de parcourir les vulnérabilités antérieures à 2019 en fonction du type de vulnérabilité. Et ce, à des fins éducatives.

OWASP

OWASP (Open Web Application Security Project) est une communauté en ligne, à but non lucratif. Créée il y a bientôt 20 ans. Son travail se focalise sur l’amélioration de la sécurité du logiciel.

Le meetup OWASP France Chapter avec Yvan Phélizot & Didier Bernaudeau[1] (https://www.meetup.com/fr-FR/owasp-france) propose un événement mensuel. Les participant.e.s peuvent présenter ou découvrir des sujets, en fonction du thème de l’édition.

L’un des projets phares de l’OWASP, l’OWASP Top 10, documente les risques de sécurité les plus critiques. Sa version française est disponible : https://owasp.org/Top10/fr. C’est un bon point de départ pour se familiariser aux problématiques de sécurité.

[1] Deux articles de Didier Bernaudeau au sujet de la sécurité dans l’écosystème JS :

Failles de sécurité

OWASP top 10 2021

L’édition 2021 du top 10 a été publiée quelques semaines avant que je finalise la rédaction de cet article.

Sur la base de la présentation de Lewis Arden, consultant en sécurité, passons en revue l’OWASP Top 10 pour l’écosystème JavaScript. Le projet “insecure-code-test” nous servira de support. Il s’agit d’une version peu sécurisée de l’application Web permettant d’évaluer les candidats pendant les workshops techniques.

A01:2021-Ruptures de contrôles d'accès

Face à des restrictions non renforcées, les attaquants accèdent à des données et fonctionnalités sans autorisation : accès aux comptes d’autres utilisateur.trice.s, à des fichiers sensibles, modification de données, changement de droits...

Pour renforcer l’authentification, l’autorisation, et la logique Métier, il existe des solutions côté serveur :

A02:2021-Défaillances cryptographiques

Il existe des librairies de chiffrement. Spécialisées et maintenues par des experts. Elles sont à privilégier pour stocker des données sensibles comme les mots de passe. La défaillance cryptographique peut compromettre tous les comptes d’une application. L’attaquant accède, simplement, à une base de données où les mots de passe sont stockés avec un salt unique. Ou pire, ils sont stockés en clair.

Il est indispensable de bien choisir les algos de chiffrement ou de hash et de les utiliser correctement. Le standard de l’industrie est `bcrypt`. Il embarque la génération du salt. Et permet de confirmer la correspondance d’un mot de passe sans exposer le secret. Écrire son propre algo de chiffrement ou réimplémenter un algo connu est fortement déconseillé.

Dans le fichier `routes.js`, la création d’utilisateur s’appuie sur les contrôleur et service responsables.

 <script> 
fastify.post("/user", async (request, reply) => { validate(request.body, reply);‍ const foundCodeworker = await findAlreadyRegistered(   request.body.codeWorkerEmail,   codeworkers ); checkEmail(foundCodeworker, reply); const createdCodeWorker = await save(request.body, codeworkers); succeedOnCreation(createdCodeWorker, reply);});
Dans le fichier `createUser.js`, chaque nouvel utilisateur est persisté en base de données MongoDB.
const { hash } = require("../../../infrastructure/webserver/utils/bcrypt");module.exports = { findAlreadyRegistered: async (email, codeworkers) => {   return await codeworkers.findOne({     email,   }); }, save: async (user, codeworkers) => {   return await codeworkers.insertOne({     email: user.codeWorkerEmail,     password: await hash(user.codeWorkerPassword),     authority: "user",   }); },};
‍Dans le fichier `logUserIn.js`, `checkPassword` confirme la correspondance entre les mots de passe.
const { verify } = require("../../../infrastructure/webserver/utils/bcrypt");‍module.exports = { find: async (email, codeworkers) => {   return await codeworkers.findOne({     email,   }); }, checkPassword: async (userPassword, foundCodeworkerPassword) => {   if (!foundCodeworkerPassword) return false;   return await verify(userPassword, foundCodeworkerPassword); },};
‍`bcrypt` fournit les fonctions :- `hash` pour calculer une chaîne de caractères aléatoires que l’on stocke- `compare` pour comparer le hash stocké et le mot de passe en clair saisi dans le formulaire
const bcrypt = require("bcrypt");‍module.exports = { hash: (password) => {   return new Promise(async (resolve, reject) => {     await bcrypt.genSalt(10, async function (err, salt) {       if (err) reject(err);       await bcrypt.hash(password, salt, function (err, hash) {         if (err) reject(err);         resolve(hash);       });     });   }); }, verify: (password, hash) => {   return new Promise(async (resolve, reject) => {     await bcrypt.compare(password, hash, function (err, result) {       if (err) reject(err);       resolve(result);     });   }); },};
fastify.post("/user", async (request, reply) => { validate(request.body, reply);‍ const foundCodeworker = await findAlreadyRegistered(   request.body.codeWorkerEmail,   codeworkers ); checkEmail(foundCodeworker, reply); const createdCodeWorker = await save(request.body, codeworkers); succeedOnCreation(createdCodeWorker, reply);});
Dans le fichier `createUser.js`, chaque nouvel utilisateur est persisté en base de données MongoDB.
const { hash } = require("../../../infrastructure/webserver/utils/bcrypt");module.exports = { findAlreadyRegistered: async (email, codeworkers) => {   return await codeworkers.findOne({     email,   }); }, save: async (user, codeworkers) => {   return await codeworkers.insertOne({     email: user.codeWorkerEmail,     password: await hash(user.codeWorkerPassword),     authority: "user",   }); },};
‍Dans le fichier `logUserIn.js`, `checkPassword` confirme la correspondance entre les mots de passe.
const { verify } = require("../../../infrastructure/webserver/utils/bcrypt");‍module.exports = { find: async (email, codeworkers) => {   return await codeworkers.findOne({     email,   }); }, checkPassword: async (userPassword, foundCodeworkerPassword) => {   if (!foundCodeworkerPassword) return false;   return await verify(userPassword, foundCodeworkerPassword); },};
‍`bcrypt` fournit les fonctions :- `hash` pour calculer une chaîne de caractères aléatoires que l’on stocke- `compare` pour comparer le hash stocké et le mot de passe en clair saisi dans le formulaire
const bcrypt = require("bcrypt");‍module.exports = { hash: (password) => {   return new Promise(async (resolve, reject) => {     await bcrypt.genSalt(10, async function (err, salt) {       if (err) reject(err);       await bcrypt.hash(password, salt, function (err, hash) {         if (err) reject(err);         resolve(hash);       });     });   }); }, verify: (password, hash) => {   return new Promise(async (resolve, reject) => {     await bcrypt.compare(password, hash, function (err, result) {       if (err) reject(err);       resolve(result);     });   }); },};
<script>

A03:2021-Injection

Les dangers du mélange de données et de code. On peut penser aux requêtes SQL, ou encore aux logs. Dans l’écosystème JavaScript, on pourra s’intéresser à l’injection en NoSQL.

L’absence d’injection SQL ne veut pas dire qu’il n'y a pas d’injection en NoSQL.

L’attaque consiste à envoyer des données hostiles pour piéger l’interpréteur. Le recours aux sélecteurs de requête de MongoDB peut provoquer de sérieux dégâts. Rendus possibles par l'exécution indésirable de commandes ou l’accès aux données sans autorisation valide.

Le code prenant directement en entrée la saisie venant du client est vulnérable si:

  1. La saisie inclut un sélecteur de requête Mongo: $ne, $lt, $gt, $eq, $regex…
  2. La saisie est directement passée en paramètre d’une méthode de collection: find, findOne, findOneAndUpdate…

L’inclusion directe dans une méthode de collection, comme `find` ou `findOne`.

Ici, l’injection NoSQL de l’opérateur `$ne` (not equal) ne permet pas de s’authentifier. Par contre, elle permet de retourner le premier utilisateur stocké dans la base de données.

Pour parer à ces attaques, on peut utiliser un schéma ou créer un validateur personnalisé avec notamment Joi (https://www.npmjs.com/package/joi).

Source : documentation de Joi, https://joi.dev/api

A7:2017 Cross-Site Scripting (XSS)

Le XSS est facile à introduire.

<pre>

const userName = location.hash.match(/userName=([^^&]*)/)[1]

// ...

div.innerHTML += `Welcome ${userName}">`

</pre>

Exécution du script: 

`http://www.vulnerable.site/#userName=<img src=malicious.site onerror=’alert(document.domain)’>`

La façon la plus simple de s’en prémunir reste encore de se méfier de toutes les entrées utilisateurs : échapper ou faire de la sanitization. Le XSS DOM est dur à contrer dans l’écosystème actuel. Chaque navigateur analyse et rend le HTML différemment. Divers contextes d’exécution et encodages co-existent. 

Utiliser les APIs sécurisés est un bon moyen d’éviter ce type de faille : 

  • préférer `innerText`à `innerHTML`
  • encodeURI

Parmi les frameworks dédiés à la désinfection d’entrée par design, on peut citer :

Comprendre les risques encourus est primordial pour s’en servir correctement.

Une autre façon de contrer le XSS, c’est de recourir à une CSP (Content Security Policy) ou stratégie de sécurité du contenu. Il s’agit du header HTTP `Content-Security-Policy` ajouté aux réponses du serveur.

A05:2021-Mauvaise configuration de sécurité

Problème très répandu, la mauvaise configuration liée à la sécurité. C’est souvent le résultat d’une configuration par défaut non sécurisée, incomplète ou ad hoc. On peut ajouter à cette liste : le stockage cloud ouvert, la mauvaise configuration des headers HTTP ou encore des messages d’erreur verbeux contenant des informations sensibles. L’OWASP propose un projet pour se familiariser avec la sécurisation des headers : Secure headers, https://owasp.org/www-project-secure-headers.

Un environnement Node bien paramétré en production avec la variable d’environnement `NODE_ENV=production` permet, notamment, d’éviter ce dernier point. A cela peut s’ajouter les vérifications dans le middleware. Il s’agit alors de s’assurer que les scripts ne sont pas exécutés avec des droits d’administrateur.

A06:2021-Composants vulnérables et obsolètes

Quand une faille connue est identifiée dans un repo Github, Dependabot (bot natif de Github) y ouvre des Pull Request. Les contributeurs n’ont plus qu’à accepter la mise à jour.

La vigilance reste de mise. Le 4 novembre 2021, la librairie “coa” a servi de point d’entrée pour une tentative de vol massif de mots de passe stockés dans les navigateurs Chrome (sources: https://www.bleepingcomputer.com/news/security/popular-coa-npm-library-hijacked-to-steal-user-passwords, https://github.com/veged/coa/issues/99). Ce dépôt, dont la dernière mise à jour officielle remonte à 2018, a été actualisé avec plusieurs publications dans la même journée. Bien que suspecte, cette activité a eu des répercussions sur des millions de projets.

Les incidents se produisent aussi sur les packages les plus populaires: express, lodash (développé plus loin avec la pollution de prototype), hapi, jquery. NPM permet de repérer et réparer les vulnérabilités connues au sein des packages installés. La commande `npm audit` liste les défaillances et leur niveau. Lancer `npm audit fix` permet de mettre à jour automatiquement la librairie, quand c’est possible.

L’OWASP (Open Web Application Security Project) propose des outils de détection des dépendances avec des vulnérabilités connues :

  • OWASP Dependency Check - identifie les dépendances et vérifie l’existence de vulnérabilités publiées
  • Retire.JS est un scanner de librairies JavaScript

Sonatype propose un outil de test d’application automatisé, le Nexus Vulnerability Scanner (NVS).

A07:2021-Identification et authentification de mauvaise qualité 

La comparaison non sécurisée d’objets peut engendrer une authentification trompeuse.

Tous les objets héritent de la classe Object en JavaScript. Vérifier les propriétés natives comme `constructor` ou les méthodes comme `hasOwnProperty` n’est pas suffisant. Il est recommandé de faire appel à une API ou librairie spécialisée, en matière d’authentification.

Node dispose d’une API, Crypto: `crypto.timingSafeEqual(a, b)` neutralise les attaques basées sur le temps (ou “timing attacks”).

Identifier et authentifier restent exigeant en termes de bonnes pratiques de développement et développement sécurisé. C’est pourquoi utiliser OAuth2 au bénéfice d’un authentification basique est fortement recommandé.

A09:2021- Carence des systèmes de contrôle et de journalisation (A10:2017 - Insufficient Logging & Monitoring)

Là encore, l’OWASP diffuse du contenu instructif. Ces deux "antisèche" nous guident pour contrer le risque d’insuffisance de logging et monitoring :

https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html

https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#handle-uncaughtexception

Les tests de sécurité applicative 

DAST : OWASP Zap 

J’ai découvert ZAP (Zed Attack Proxy) dans le cadre du workshop “Become a hacker” de Paul Molin, Web Application Security Evangelist chez Theodo. 

ZAP est proxy local installé sur votre machine. Ce qui permet à ZAP de générer un arbre représentant l'application analysée.

La liste des fonctionnalités regroupe :

  • Proxy, toutes les requêtes et réponses passent par ZAP
  • Passive scanning,  toutes les requêtes et réponses sont analysées pour détecter les vulnérabilités
  • Spidering, toutes les réponses sont “parsées” pour découvrir tout nouveau contenu
  • Active scanning,  toutes les requêtes sont rejouées pour identifier les plus grandes vulnérabilités
  • Fuzzing, les requêtes sont aléatoirement modifiées
  • Scripts, pour étendre les fonctionnalités

Vous pouvez retrouver les points-clé du workshop sur ce blog : https://blog.theodo.com/2016/04/how-to-become-a-hacker-in-10-minutes.

Il existe aussi une CLI pour brancher ZAP à votre usine logicielle (https://www.zaproxy.org/docs/desktop/cmdline).

SAST : Node Secure

Nsecure - Node.js CLI that allow you to deeply analyze the dependency tree of a given npm package or a directory, github node-secure

Dans le hors-série à paraître l’été prochain, nous reviendrons sur Nsecure et la sécurité de l’écosystème de Node.js dans le cadre de l’interview de Thomas Gentilhomme. Mentor de devs, il est aussi membre du Node.js security working group.

Nous avons tout un panel d’outils à notre disposition pour nous alerter en cas de risques détectés. La façon la plus simple de les éviter c’est de bien connaître le langage pour nous passer de ses fonctionnalités qui sont des vecteurs d’attaque.

Les fonctionnalités dangereuses de JavaScript

Bannir les comparaisons faibles avec le double égal pour leur préférer la stricte égalité ou `Objetc.is(firstObject, secondObject)`

Éviter les fonctions dangereuses comme `eval(alert(‘Alert !’))` qui acceptent le code sous forme de chaîne de caractères et l’exécutent.

https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-3-javascript-encode-before-inserting-untrusted-data-into-javascript-data-values

Invoquer le mode strict avec "use strict" (sauf en cas de rétro-compatibilité  antérieure à l’ES5 requise) lève des erreurs en cas de “mauvaise syntaxe”.

Pour éliminer toutes les fonctionnalités dangereuses, ESLint repère les anti-patterns dans le code. Ajouter ces quelques règles au fichier `.eslintrc.json` :

  • "no-eval": intercepte tous les appels directs à la fonction `eval`
  • "no-implied-eval": avertit en cas d’utilisation des variantes risquées de `setTimeout` et `setInterval`
  • "no-new-func":  empêche le code d’utiliser la fonction `constructor`
  • "eqeqeq": force l’utilisation de l’égalité stricte dans toutes les situations, sauf si vous optez pour une gestion spécifique de l’égalité avec `null`

<pre>

{

 "env": {

   "browser": true,

   "commonjs": true,

   "es2020": true

 },

 "parserOptions": {

   "ecmaVersion": 11

 },

 "extends": "eslint:recommended",

 "rules": {

   "no-eval": "error",

   "no-implied-eval": "error",

   "no-new-func": "error",

   "eqeqeq": ["error", "always"]

 }

}

</pre>

Pour vérifier l’égalité stricte entre objets, préférez `Object.is`. Cette fonction fait une comparaison stricte, sauf dans des cas à la marge, en ce qui concerne les nombres. 

Il est recommandé de toujours vérifier le type des données provenant du client. Des packages de validation et désinfection de formulaires bloqueront toutes tentatives d’altération du comportement de l’application.

Pour renforcer la sécurité de votre code source, il existe des packages dédiés référencés dans la liste awesome Node.js security (Nodejs Security Cheat Sheet, do not use dangerous functions) :

  • le plugin de sécurité d’eslint : https://www.npmjs.com/package/eslint-plugin-security
  • js-x-ray, le scanner SAST pour JavaScript et Node.js, capable de détecter divers patterns de code malicieux bien connus (*) : https://github.com/fraxken/js-x-ray

(*) import non sécurisé, instruction non sécurisée, regex non sécurisée, littéraux encodés, codes minifiés et offusqués

Dans la continuité des points d’attention propres à notre langage, la pollution de prototype est à garder en tête dans les interactions entre le client et le serveur.

Prototype pollution : de quoi parle-t-on ?

La chaîne de prototypes : particularité de JavaScript

L’une des caractéristiques principales des langages orientés objet est l’héritage. L’héritage est construit par les relations entre les classes(*). JavaScript repose sur la chaîne des prototypes. Tout objet peut être à la fois objet et prototype. Chaque objet possède la propriété `__proto__` qui retourne son prototype (un objet entier).

Les objets ont leurs propres propriétés et des propriétés héritées grâce à la chaîne de prototypes. Les propriétés propres à l’objet sont déclarées à la création de l’objet. Elles peuvent aussi être ajoutées lors de l’exécution du code. Les propriétés héritées qui, quand elles sont définies, ne sont modifiées que pour l’objet propriétaire. La chaîne de prototypes est donc dynamique.

Sa nature dynamique permet de modifier un objet de la chaîne, n’importe quand. Ce qui peut rendre le code d’une application vulnérable aux attaques de pollution de prototype (prototype pollution).

Par le passé, l’altération des méthodes natives de JavaScript a pu entraîner un déni de service : envoyer une chaîne de caractère là où une fonction était attendue suffisait à lever une erreur, avec la version 4.17.4 de lodash. La navigation d’une application web était alors rendue impossible : toutes les pages affichaient `TypeError: Object.prototype.toString.call is not a function`.

Pour remédier à cette vulnérabilité, il fallait installer une version de lodash plus mature en termes de sécurité.

https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#monitor-the-event-loop

https://www.npmjs.com/package/toobusy-js

(*) Les classes apparues en JavaScript avec l’ES6 ne sont que du sucre syntaxique pour définir la chaîne de prototypes. JavaScript utilise toujours l’héritage prototypal.

(source: https://portswigger.net/daily-swig/prototype-pollution-the-dangerous-and-underrated-vulnerability-impacting-javascript-applications)

La fixation de session (Session fixation) consiste à ajouter les identifiants d’un compte légitime à la chaîne de prototype. En l’absence d’identifiants dans la requête de connexion, les identifiants usurpés peuvent être envoyés au serveur. L’attaquant a alors accès aux données confidentielles du compte piraté.

Pour éviter d’introduire une vulnérabilité à cette pollution, quelques bonnes pratiques :

  • Geler les propriétés avec `Object.freeze (Object.prototype)` :
  • Procéder à la validation des saisies JSON en adéquation avec le schéma défini 
  • Éviter d’utiliser des fonctions de fusion récursives de manière non sécurisée
  • Utiliser des objets sans prototype tel que `Object.create(null)` pour éviter d’affecter la chaîne de prototypes
  • Préférer `Map` à Object`
  • Mettre régulièrement à jour les nouveaux patches des librairies installées

Pour contrer toutes attaques de pollution de prototypes, il existe des packages dédiés référencés dans la liste awesome Node.js security :

Monkey patching

Dans le cadre d’un BBL (Brown Bag Lunch) sur les tests d’approbation et les tests de caractérisation, je présentais mon exemple de test d’approbation manuel. Un des participants a alors évoqué le “monkey patching”, ce qui a piqué ma curiosité. Mon test manuel avait pour unique objectif de rediriger la sortie de l’application. Le monkey patching, pratique déconseillée, dans la majorité des cas, ne répondait pas au besoin (source: https://www.audero.it/blog/2016/12/05/monkey-patching-javascript/).

Secure TDD

Et s’il existait une méthode de développement qui permettrait de prévenir les vulnérabilités ? Un TDD de la sécurité ?

Ce n'est pas du TDD à proprement parler. C'est surtout une méthode de tests automatisés branchés à l'usine logicielle. Son usage est recommandé en staging ou QA, déconseillé pour la prod.Le principe est de réaliser des tests automatisés, à l’aide d’outils d’analyse en tout genre :

  • Statique :SonarQube, downloads - compatible avec 15 langages
  • Dynamique : OWASP Zap - pratique pour les débutants en sécurité principalement focalisé sur le Top 10 OWASP, utilise Selenium en interne  pour la navigation (spidering de site), paramétrable avec les APIs de ZAP, WireShark - standard de l'industrie, libre et gratuit. Les deux solutions proposent les options in-scope et out-of-scope pour limiter le périmètre du scan à certaines URLs
  • Forensique : Splunk - data leak, particulièrement efficace pour l’analyse des logs, permet de créer des dashboards

Quelques références :

Secure by design

Le concept du “Secure by design”, c’est de mettre la qualité au service de la sécurité. Les pratiques du Craft (ou Software Craftsmanship) comme le pair-programming, le TDD et le Clean Code viennent sécuriser l’application. Les développeurs questionnent le Métier sur les cas extrêmes, les limites des cas d’utilisation. Le but étant de renforcer le cœur de métier et les fonctionnalités qui génèrent des revenus. 

(sources: https://cotonne.github.io/agile/security/craft/secure-coding/secure-by-design/2018/06/07/crafting-secure-software.html, https://fr.slideshare.net/YvanPHELIZOT/crafting-secure-software-dddeu-2019)

Pour progresser

Il existe de nombreuses ressources pour monter en compétences sur les problématiques de sécurité.

La chasse au trésor pour découvrir comment se blinder contre les vulnérabilités :

Les ressource OWASP :

  • OWASP NodeGoat, https://github.com/OWASP/NodeGoat - Ce projet fournit un environnement pour apprendre comment les risques du Top 10 s’appliquent aux web apps développées avec Node. Et comment les éradiquer efficacement.
  • OWASP Juice Shop, https://github.com/bkimminich/juice-shop - Cette web app volontairement vulnérable sert de support d’entraînement à la sécurité. Elle regroupe tout le Top 10 et d’autres failles de sécurité sévères.
  • DomGoat, https://domgo.at/cxss/intro - Ce site liste des informations et exercices sur les différentes sources et exemples de XSS.

Pour aller plus loin

Livres en anglais : “Building secure and reliable systems”, “Thinking Security: Stopping Next Year’s Hackers”

Articles en anglais : “7 places to do automated Security Tests”, “Security is everybody’s job [1/6]”

Conclusion

Si vous souhaitez vous familiariser avec la cyber sécurité, je vous recommande Cyber security for beginners de Raef Meeuwisse. Nina Cercy, experte en cybersécurité, nous l’a conseillé, lors d’un webinar organisé par l’Ada Tech School (“Cybersécurité : les métiers du futur ?”, disponible sur Youtube). Elle affirmait que ce livre permettait d’obtenir une structure mentale puissante sur le sujet.

La sécurité dans l’écosystème JavaScript est un vaste sujet. Sujet qui pourrait remplir, à lui seul, un numéro entier de Programmez!. J’espère que cette vue d’ensemble d’une sélection de moyens de sécuriser votre appli JS vous aura donné envie d’en apprendre toujours plus. Et surtout, de pouvoir les mettre en pratique.

CodeWorks, un modèle d'ESN qui agit pour plus de justice sociale.

Notre Manifeste est le garant des droits et devoirs de chaque CodeWorker et des engagements que CodeWorks a vis-à-vis de chaque membre.
Il se veut réaliste, implémenté, partagé et inscrit dans une démarche d'amélioration continue.

Rejoins-nous !

Tu veux partager tes connaissances et ton temps avec des pairs empathiques, incarner une vision commune de l'excellence logicielle et participer activement à un modèle d'entreprise alternatif, rejoins-nous.