Activité Pratique : Renforcement de la Sécurité d’un Formulaire de Connexion
Dans cette activité, nous allons prendre un formulaire de connexion de base et y appliquer des bonnes pratiques pour renforcer sa sécurité. L’objectif est de prévenir les attaques courantes comme le XSS, le CSRF, et le brute force, tout en protégeant les données utilisateur.
Préparation du Formulaire de Connexion de Base
Code HTML de départ :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connexion</title>
</head>
<body>
<form id="loginForm" action="/login" method="POST">
<label for="username">Nom d'utilisateur :</label>
<input type="text" id="username" name="username" required>
<label for="password">Mot de passe :</label>
<input type="password" id="password" name="password" required>
<button type="submit">Se connecter</button>
</form>
</body>
</html>
Code Node.js de départ :
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/login', (req, res) => {
const { username, password } = req.body;
// Vérification des identifiants (exemple fictif)
if (username === 'admin' && password === 'password123') {
res.send('Connexion réussie');
} else {
res.status(401).send('Nom d’utilisateur ou mot de passe incorrect');
}
});
app.listen(3000, () => {
console.log('Serveur en cours d’exécution sur http://localhost:3000');
});
Ce formulaire fonctionne, mais il est vulnérable à plusieurs attaques. Nous allons maintenant y ajouter des mesures de sécurité.
Mesures de Sécurisation
a. Protection contre les Attaques XSS
Vulnérabilité XSS :
Les attaquants peuvent injecter des scripts malveillants dans les champs de formulaire, qui s’exécuteront sur le navigateur de la victime.
Solution :
- Valider et échapper toutes les entrées utilisateur côté serveur.
- Ajouter une Content Security Policy (CSP) pour restreindre les sources de scripts.
Mise en œuvre dans Node.js :
const sanitizeHtml = require('sanitize-html');
app.post('/login', (req, res) => {
const username = sanitizeHtml(req.body.username);
const password = sanitizeHtml(req.body.password);
if (username === 'admin' && password === 'password123') {
res.send('Connexion réussie');
} else {
res.status(401).send('Nom d’utilisateur ou mot de passe incorrect');
}
});
Ajout d’une CSP dans les headers HTTP :
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self';"
);
next();
});
b. Protection contre les Attaques CSRF
Vulnérabilité CSRF :
Un attaquant peut forcer un utilisateur authentifié à envoyer une requête malveillante (par exemple, en cliquant sur un lien malicieux).
Solution :
- Utiliser des jetons CSRF pour valider que chaque requête POST provient de l’utilisateur légitime.
Installation et utilisation de csurf
:
npm install csurf cookie-parser
Ajout dans l’application :
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.get('/login', (req, res) => {
res.send(`
<form id="loginForm" action="/login" method="POST">
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
<label for="username">Nom d'utilisateur :</label>
<input type="text" id="username" name="username" required>
<label for="password">Mot de passe :</label>
<input type="password" id="password" name="password" required>
<button type="submit">Se connecter</button>
</form>
`);
});
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === 'admin' && password === 'password123') {
res.send('Connexion réussie');
} else {
res.status(401).send('Nom d’utilisateur ou mot de passe incorrect');
}
});
c. Limitation des Attaques par Force Brute
Vulnérabilité :
Les attaquants peuvent essayer plusieurs combinaisons de mots de passe jusqu’à en trouver un correct.
Solution :
- Limiter le nombre de tentatives de connexion.
- Implémenter un délai ou un blocage temporaire après plusieurs échecs.
Installation et utilisation de express-rate-limit
:
npm install express-rate-limit
Ajout dans l’application :
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // Limite à 5 tentatives
message: 'Trop de tentatives de connexion. Réessayez plus tard.',
});
app.post('/login', loginLimiter, (req, res) => {
const { username, password } = req.body;
if (username === 'admin' && password === 'password123') {
res.send('Connexion réussie');
} else {
res.status(401).send('Nom d’utilisateur ou mot de passe incorrect');
}
});
d. Utilisation de Cookies Sécurisés
Vulnérabilité :
Les cookies peuvent être volés via des attaques XSS ou envoyés sur des connexions non sécurisées.
Solution :
- Ajouter les options
HttpOnly
,Secure
, etSameSite
aux cookies de session.
Exemple :
const session = require('express-session');
app.use(session({
secret: 'votre_secret',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: false, // Passez à true si vous utilisez HTTPS
sameSite: 'strict',
},
}));
Résultat Final
Avec ces améliorations, notre formulaire de connexion est désormais protégé contre :
- XSS : Validation des entrées et ajout d’une CSP.
- CSRF : Implémentation de jetons CSRF.
- Attaques par force brute : Limitation des tentatives de connexion.
- Vol de session : Cookies sécurisés.
Tester l’application :
- Lancez le serveur :
node app.js
- Accédez à
http://localhost:3000/login
pour tester le formulaire sécurisé. - Effectuez des tests avec des attaques simulées (par exemple, en injectant des scripts ou en envoyant plusieurs requêtes incorrectes).
Ces bonnes pratiques renforcent significativement la sécurité des systèmes d’authentification tout en maintenant une expérience utilisateur fluide.