109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
var express = require('express');
|
|
var router = express.Router();
|
|
var bcrypt = require('bcrypt');
|
|
var rateLimit = require('express-rate-limit');
|
|
|
|
// --- Base de données simulée ---
|
|
const db = {
|
|
users: [
|
|
{
|
|
id: 1,
|
|
username: 'admin',
|
|
password_vulnerable: 'password123',
|
|
hashedPassword: bcrypt.hashSync('password123', 12)
|
|
}
|
|
]
|
|
};
|
|
|
|
/* GET home page */
|
|
router.get('/', function(req, res, next) {
|
|
res.render('index', { title: 'Express' });
|
|
});
|
|
|
|
// ==========================================================
|
|
// ## 👎 DÉMONSTRATION VULNÉRABLE (A07)
|
|
// ==========================================================
|
|
|
|
router.post('/login-vulnerable', (req, res) => {
|
|
// !! REPOSE SUR express.json() et express.session() définis dans app.js !!
|
|
const { username, password } = req.body;
|
|
|
|
const user = db.users.find(u => u.username === username);
|
|
|
|
// FAILLE 1 : Énumération de comptes
|
|
if (!user) {
|
|
return res.status(404).send('Erreur : Utilisateur non trouvé.');
|
|
}
|
|
|
|
// FAILLE 2 : Stockage et comparaison en clair
|
|
if (user.password_vulnerable !== password) {
|
|
return res.status(400).send('Erreur : Mot de passe incorrect.');
|
|
}
|
|
|
|
// FAILLE 3 : Fixation de session
|
|
req.session.userId = user.id;
|
|
req.session.username = user.username;
|
|
|
|
console.log(`[VULNÉRABLE] Session AVANT login: ${req.sessionID}`);
|
|
console.log(`[VULNÉRABLE] Session APRÈS login: ${req.sessionID} (INCHANGÉE)`);
|
|
|
|
res.send(`Connexion (vulnérable) réussie pour ${user.username} !`);
|
|
});
|
|
|
|
// ==========================================================
|
|
// ## DÉMONSTRATION CORRIGÉE (A07)
|
|
// ==========================================================
|
|
|
|
// CORRECTION 4 : Limitation de tentatives (Rate Limiting)
|
|
const loginLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 10, // Limite à 10 tentatives par IP par fenêtre de 15 min
|
|
message: 'Trop de tentatives de connexion. Réessayez dans 15 minutes.'
|
|
});
|
|
|
|
// Applique le limiteur uniquement à ce point de terminaison
|
|
router.use('/login-securise', loginLimiter);
|
|
|
|
router.post('/login-securise', async (req, res) => {
|
|
// !! REPOSE SUR express.json() et express.session() définis dans app.js !!
|
|
const { username, password } = req.body;
|
|
|
|
const user = db.users.find(u => u.username === username);
|
|
|
|
// CORRECTION 1 & 2 : Pas d'énumération + Hachage fort
|
|
const match = user ? await bcrypt.compare(password, user.hashedPassword) : false;
|
|
|
|
if (!match) {
|
|
return res.status(401).send('Identifiants invalides.');
|
|
}
|
|
|
|
// CORRECTION 3 : Régénération de la session
|
|
const oldSessionId = req.sessionID;
|
|
req.session.regenerate((err) => {
|
|
if (err) {
|
|
console.error(err);
|
|
return res.status(500).send('Erreur lors de la régénération de session.');
|
|
}
|
|
req.session.userId = user.id;
|
|
req.session.username = user.username;
|
|
|
|
console.log(`[SÉCURISÉ] Session AVANT login: ${oldSessionId}`);
|
|
console.log(`[SÉCURISÉ] Session APRÈS login: ${req.sessionID} (CHANGÉE)`);
|
|
|
|
res.send(`Connexion (sécurisée) réussie pour ${user.username} !`);
|
|
});
|
|
});
|
|
|
|
// --- Point de déconnexion ---
|
|
router.get('/logout', (req, res) => {
|
|
// !! REPOSE SUR express.session() défini dans app.js !!
|
|
req.session.destroy((err) => {
|
|
if (err) {
|
|
return res.status(500).send('Impossible de se déconnecter.');
|
|
}
|
|
res.clearCookie('connect.sid'); // Nom par défaut du cookie de session
|
|
res.send('Déconnecté avec succès.');
|
|
});
|
|
});
|
|
|
|
module.exports = router; |