Sécurité Web en 2025 : OWASP Top 10 et Comment Protéger Vos Applications JavaScript
Salut HaWkers, avec les cyberattaques augmentant exponentiellement, la sécurité n'est plus la responsabilité des seuls spécialistes. Tout développeur doit connaître l'OWASP Top 10 et implémenter les protections de base.
Explorons les vulnérabilités les plus communes et comment protéger vos applications JavaScript.
OWASP Top 10 : Les Vulnérabilités Critiques
const owaspTop10_2025 = {
1: 'Broken Access Control',
2: 'Cryptographic Failures',
3: 'Injection (SQL, XSS, etc)',
4: 'Insecure Design',
5: 'Security Misconfiguration',
6: 'Vulnerable Components',
7: 'Authentication Failures',
8: 'Software and Data Integrity',
9: 'Security Logging Failures',
10: 'Server-Side Request Forgery (SSRF)'
};
1. Prévenir le XSS (Cross-Site Scripting)
// ❌ VULNÉRABLE - Ne jamais faire ça !
function renderUserInput(input) {
document.getElementById('output').innerHTML = input;
// Si input = "<script>alert('XSS')</script>"
// Le script s'exécute !
}
// ✅ SÉCURISÉ - Sanitize input
import DOMPurify from 'dompurify';
function renderUserInputSafe(input) {
const clean = DOMPurify.sanitize(input);
document.getElementById('output').innerHTML = clean;
}
// ✅ ENCORE PLUS SÉCURISÉ - Utilisez textContent
function renderText(input) {
document.getElementById('output').textContent = input;
// Le HTML n'est pas interprété
}
// React échappe automatiquement
function UserProfile({ username }) {
return <div>{username}</div>; // Safe !
}2. Prévention de l'Injection SQL
// ❌ VULNÉRABLE - Concaténation de string
async function getUser(userId) {
const query = `SELECT * FROM users WHERE id = ${userId}`;
// Si userId = "1 OR 1=1", retourne TOUS les utilisateurs !
return await db.query(query);
}
// ✅ SÉCURISÉ - Prepared statements
async function getUserSafe(userId) {
const query = 'SELECT * FROM users WHERE id = ?';
return await db.query(query, [userId]);
}
// ✅ ORM encore plus sécurisé
async function getUserORM(userId) {
return await User.findById(userId);
// L'ORM sanitize automatiquement
}
3. Authentification Sécurisée
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
class AuthService {
// Enregistrer un utilisateur
async register(email, password) {
// Valider la force du mot de passe
if (password.length < 12) {
throw new Error('Le mot de passe doit avoir minimum 12 caractères');
}
// Hash avec bcrypt (JAMAIS sauvegarder le mot de passe en clair !)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
await db.users.create({
email,
password: hashedPassword
});
}
// Login
async login(email, password) {
const user = await db.users.findOne({ email });
if (!user) {
// ✅ Message générique (ne révèle pas si l'email existe)
throw new Error('Identifiants invalides');
}
// Vérifier le mot de passe
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
throw new Error('Identifiants invalides');
}
// JWT avec expiration courte
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
return { token, user: { id: user.id, email: user.email } };
}
// Vérifier le token
verifyToken(token) {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch {
throw new Error('Token invalide');
}
}
}4. Protection CSRF
// Backend - Express
import csrf from 'csurf';
const csrfProtection = csrf({ cookie: true });
app.post('/api/transfer', csrfProtection, async (req, res) => {
// Token CSRF validé automatiquement
await processTransfer(req.body);
res.json({ success: true });
});
// Frontend - Envoyer le token
async function transferMoney(amount, to) {
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
await fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({ amount, to })
});
}
5. Validation d'Input
import { z } from 'zod';
// Schéma de validation
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18).max(120),
username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/)
});
// Valider l'input
async function createUser(data) {
try {
// Valide et parse
const validated = userSchema.parse(data);
// ✅ Données garanties valides
return await db.users.create(validated);
} catch (error) {
throw new Error('Données invalides');
}
}6. Rate Limiting
import rateLimit from 'express-rate-limit';
// Prévenir le brute force
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 tentatives
message: 'Trop de tentatives de connexion. Réessayez dans 15 minutes.'
});
app.post('/api/login', loginLimiter, async (req, res) => {
// Logique de login
});
// Rate limit général
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100 // 100 requêtes
});
app.use('/api/', apiLimiter);7. Secure Headers
import helmet from 'helmet';
// Express - Helmet configure les headers sécurisés
app.use(helmet());
// Headers importants :
const securityHeaders = {
'Content-Security-Policy': "default-src 'self'",
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'Strict-Transport-Security': 'max-age=31536000',
'X-XSS-Protection': '1; mode=block'
};Checklist de Sécurité
const securityChecklist = [
'✅ Valider TOUT input utilisateur',
'✅ Utiliser prepared statements (SQL)',
'✅ Sanitizer l\'output HTML',
'✅ Implémenter le rate limiting',
'✅ HTTPS en production (toujours !)',
'✅ Passwords hashés avec bcrypt',
'✅ JWT avec expiration courte',
'✅ CSRF tokens dans les formulaires',
'✅ Security headers (Helmet)',
'✅ Dépendances à jour',
'✅ Secrets en variables d\'environnement',
'✅ Logging des événements de sécurité',
'✅ CORS configuré correctement'
];La sécurité n'est pas optionnelle en 2025. Avec des attaques de plus en plus sophistiquées, tout développeur est responsable du code sécurisé.
C'est parti !
🎯 Les Développeurs Sécurisés Ont Plus de Valeur
La connaissance en sécurité différencie les développeurs juniors des professionnels complets. Investissez dans une connaissance solide.
Commencez maintenant :
- 9,90€ (paiement unique)

