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, et SameSite 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 :

  1. XSS : Validation des entrées et ajout d’une CSP.
  2. CSRF : Implémentation de jetons CSRF.
  3. Attaques par force brute : Limitation des tentatives de connexion.
  4. Vol de session : Cookies sécurisés.

Tester l’application :

  1. Lancez le serveur :
    node app.js
  2. Accédez à http://localhost:3000/login pour tester le formulaire sécurisé.
  3. 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.