Sécurité des applications TypeScript : top 10 des vulnérabilités en pratique
TypeScript est une surcouche de JavaScript qui ajoute un typage statique. De nombreux développeurs pensent à tort que le typage strict rend automatiquement l'application sécurisée. Ce n'est pas le cas. Le compilateur TypeScript vérifie les types, mais ne protège pas contre les erreurs logiques, les injections ou les problèmes d'authentification. Dans cet article, nous allons analyser les 10 vulnérabilités les plus courantes dans les applications TypeScript, montrer des exemples de code et donner des recommandations concrètes pour la protection. Cet article sera utile aussi bien aux développeurs débutants qu'aux développeurs expérimentés.
1. Cross-Site Scripting (XSS) via un mauvais traitement des entrées
Le XSS reste l'une des vulnérabilités les plus dangereuses. TypeScript n'échappe pas automatiquement la sortie. Si vous insérez des données utilisateur dans un modèle HTML sans assainissement, vous ouvrez la porte aux attaques.
Code vulnérable
function renderComment(comment: string): string { return `<div>${comment}</div>`;}
// L'attaquant transmet : <script>alert('XSS')</script>Code corrigé
import { escape } from 'lodash';
function renderComment(comment: string): string { const safeComment = escape(comment); return `<div>${safeComment}</div>`;}Recommandation : Utilisez toujours des bibliothèques d'échappement (DOMPurify, lodash.escape) ou des moteurs de templates avec échappement automatique (React JSX, Handlebars).
2. Injections SQL via des templates de chaînes
TypeScript permet d'utiliser des templates de chaînes pour construire des requêtes SQL. C'est un chemin direct vers l'injection SQL si vous insérez des données utilisateur.
Code vulnérable
async function getUser(id: string): Promise<User | null> { const query = `SELECT * FROM users WHERE id = '${id}'`; const result = await db.query(query); return result.rows[0];}Code corrigé
async function getUser(id: string): Promise<User | null> { const query = 'SELECT * FROM users WHERE id = $1'; const result = await db.query(query, [id]); return result.rows[0];}Recommandation : Utilisez des requêtes paramétrées ou un ORM (TypeORM, Prisma, Sequelize). Ne concaténez jamais de chaînes pour le SQL.
3. Comparaison non sécurisée des mots de passe
Une erreur dans l'implémentation de la comparaison des mots de passe peut entraîner des attaques temporelles (timing attacks). Les développeurs TypeScript utilisent souvent l'opérateur === pour comparer les hachages, ce qui n'est pas sécurisé.
Code vulnérable
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { const inputHash = await bcrypt.hash(inputPassword, 10); return inputHash === storedHash; // Vulnérabilité : timing attack}Code corrigé
import bcrypt from 'bcrypt';
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { return bcrypt.compare(inputPassword, storedHash);}Recommandation : Utilisez les fonctions de comparaison intégrées des bibliothèques de hachage (bcrypt.compare, argon2.verify). Elles sont protégées contre les attaques temporelles.
4. Pollution de prototype via la fusion récursive d'objets
Les applications TypeScript utilisent souvent des fonctions de copie profonde ou de fusion d'objets. Si les clés ne sont pas contrôlées, un attaquant peut polluer le prototype Object.prototype.
Code vulnérable
function merge(target: Record<string, any>, source: Record<string, any>) { for (const key in source) { if (typeof source[key] === 'object') { target[key] = merge(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target;}
// L'attaquant pe