Authentification et autorisation
Vulnérabilités liées à l'authentification et à l'autorisation
Notions théoriques
L’authentification et l’autorisation sont 2 concepts essentiels en sécurité informatique.
Une mauvaise implémentation peut entraîner des failles critiques, comme l’accès non autorisé à des données sensibles.
Authentification vs Autorisation
Concept | Définition |
---|---|
Authentification | Vérifie l’identité d’un utilisateur (ex : login/mot de passe). |
Autorisation | Détermine ce qu’un utilisateur peut faire après authentification. |
Vulnérabilités courantes
Attaque par force brute
- Un attaquant essaie de deviner un mot de passe en testant de nombreuses combinaisons.
- Exemple : Tester
admin
,admin123
,password
,123456
.
Attaque par interception (Man-in-the-Middle)
- Un attaquant intercepte les identifiants si la connexion n’est pas sécurisée (ex : HTTP au lieu de HTTPS).
- Exemple : Un mot de passe envoyé en clair peut être volé sur un réseau Wi-Fi public.
Réutilisation de sessions
- Un attaquant vole un cookie de session pour se faire passer pour un utilisateur légitime.
- Exemple : Si un site ne régénère pas la session après connexion, un attaquant peut exploiter un cookie volé.
Contournement de l’authentification
- Une faille dans le code permet d’accéder à un compte sans mot de passe.
- Exemple : Une URL comme
site.com/profil?id=1
permet d’accéder à d’autres comptes en changeantid
.
Escalade de privilèges
- Un utilisateur normal obtient des droits administrateur par erreur.
- Exemple : Modifier une requête HTTP pour passer de
role=user
àrole=admin
.
Bonnes pratiques pour sécuriser l’authentification
✔️ Utiliser HTTPS pour chiffrer les communications.
✔️ Limiter les tentatives de connexion (ex : 3 essais avant blocage).
✔️ Utiliser un hachage sécurisé pour stocker les mots de passe (bcrypt
, argon2
).
✔️ Régénérer la session après connexion pour éviter le vol de session.
✔️ Vérifier les permissions sur chaque action pour éviter l’escalade de privilèges.
Exemple pratique
Nous allons analyser un système d’authentification vulnérable et le corriger.
Code d’un système vulnérable
<?php
$pdo = new PDO("mysql:host=localhost;dbname=security_test", "root", "");
// ⚠️ Problème : Requête non sécurisée
$query = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . $_POST['password'] . "'";
$result = $pdo->query($query);
if ($result->rowCount() > 0) {
echo "✔️ Connexion réussie !";
} else {
echo "❌ Échec de la connexion.";
}
?>
Problèmes de sécurité
- Injection SQL possible (
' OR '1'='1
permet de se connecter sans mot de passe). - Mot de passe stocké en clair (un attaquant peut le voler si la base est compromise).
- Pas de limitation des tentatives (vulnérable aux attaques par force brute).
Code sécurisé
<?php
$pdo = new PDO("mysql:host=localhost;dbname=security_test", "root", "");
// ✔️ Utilisation de requêtes préparées
$query = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$query->execute([$_POST['username']]);
$user = $query->fetch();
if ($user && password_verify($_POST['password'], $user['password'])) {
session_start();
$_SESSION['user'] = $user['username'];
echo "✔️ Connexion réussie !";
} else {
echo "❌ Échec de la connexion.";
}
?>
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Dans ce TP, vous allez analyser et corriger un formulaire vulnérable pour le rendre sécurisé :
✔️ Protéger un site contre les injections SQL
✔️ Empêcher les attaques XSS
✔️ Sécuriser les mots de passe et l’authentification
✔️ Tester les vulnérabilités et vérifier les protections
Étape 1 : Préparer l’environnement
Nous allons utiliser un serveur local sous Debian avec Apache, PHP et MySQL.
Installation des services nécessaires
Ouvrez un terminal et exécutez les commandes suivantes pour installer les outils essentiels :
sudo apt update
sudo apt install apache2 php php-mysql mariadb-server -y
Démarrer Apache et MySQL
sudo systemctl start apache2
sudo systemctl start mariadb
Sécuriser MySQL
optionnel mais recommandé
sudo mysql_secure_installation
Répondez aux questions pour définir un mot de passe root et sécuriser l’accès.
Étape 2 : Créer une BD vulnérable
Nous allons créer une table users avec des identifiants stockés sans protection (ce qui est une mauvaise pratique).
Accéder à MySQL
sudo mysql -u root -p
Créer une base de données et une table
Dans MySQL, exécutez ces commandes :
CREATE DATABASE security_test;
USE security_test;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL
);
INSERT INTO users (username, password) VALUES ('admin', '123456'), ('alice', 'password');
Vérifier les utilisateurs
Vérifier que les utilisateurs sont bien enregistrés
SELECT * FROM users;
Étape 3 : Créer un formulaire vulnérable
Nous allons créer une page de connexion non sécurisée.
Créer un fichier login.php
dans /var/www/html/
sudo nano /var/www/html/login.php
Ajouter le code suivant (⚠️ VULNÉRABLE ⚠️)
<?php
$pdo = new PDO("mysql:host=localhost;dbname=security_test", "root", "");
// ⚠️ REQUÊTE NON SÉCURISÉE (VULNÉRABLE À L'INJECTION SQL)
$query = "SELECT * FROM users WHERE username = '" . $_GET['username'] . "' AND password = '" . $_GET['password'] . "'";
$result = $pdo->query($query);
if ($result->rowCount() > 0) {
echo "✔️ Connexion réussie !";
} else {
echo "❌ Échec de la connexion.";
}
?>
Créer une page HTML pour tester la connexion
sudo nano /var/www/html/index.html
Ajouter le formulaire suivant
<!DOCTYPE html>
<html>
<head>
<title>Connexion</title>
</head>
<body>
<h2>Connexion</h2>
<form action="login.php" method="GET">
<label>Nom d'utilisateur :</label>
<input type="text" name="username">
<label>Mot de passe :</label>
<input type="password" name="password">
<button type="submit">Se connecter</button>
</form>
</body>
</html>
Étape 4 : Tester les failles de sécurité
Nous allons exploiter les vulnérabilités du formulaire.
Tester une injection SQL
- Ouvrez un navigateur et entrez l’URL suivante :
http://<IP_DU_SERVEUR>/login.php?username=admin'--&password=123
- Résultat attendu :
✔️ Connexion réussie !
(L’attaque fonctionne car--
commente le reste de la requête SQL.)
Tester une attaque XSS
- Essayez cette URL :
http://<IP_DU_SERVEUR>/login.php?username=<script>alert('Hacked')</script>&password=123
- Résultat attendu :
Une alerte JavaScript apparaît dans le navigateur.
Étape 5 : Sécuriser le formulaire
Nous allons corriger les failles en appliquant les bonnes pratiques.
Modifier login.php
pour sécuriser les requêtes
sudo nano /var/www/html/login.php
Remplacer le code par une version sécurisée
<?php
$pdo = new PDO("mysql:host=localhost;dbname=security_test", "root", "");
// ✔️ UTILISATION DE REQUÊTES PRÉPARÉES (PROTECTION CONTRE L'INJECTION SQL)
$query = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$query->execute([$_POST['username'], $_POST['password']]);
if ($query->rowCount() > 0) {
echo "✔️ Connexion réussie !";
} else {
echo "❌ Échec de la connexion.";
}
?>
Modifier le formulaire pour utiliser POST
sudo nano /var/www/html/index.html
Corriger le formulaire
<form action="login.php" method="POST">
<label>Nom d'utilisateur :</label>
<input type="text" name="username">
<label>Mot de passe :</label>
<input type="password" name="password">
<button type="submit">Se connecter</button>
</form>
Étape 6 : Vérifier la correction
Vérifier que les failles sont corrigées
Tester à nouveau les injections SQL
- Essayez
admin'--
→ Échec de la connexion ✔️ - Essayez
admin" OR 1=1 --
→ Échec de la connexion ✔️
Tester à nouveau les attaques XSS
- Essayez
<script>alert('Hacked')</script>
→ Aucun script ne s’exécute ✔️
Vérifier que le mot de passe n’apparaît plus dans l’URL.
Étape 7 : Sécuriser les mots de passe
Nous allons maintenant hacher les mots de passe pour éviter de les stocker en clair.
Modifier la base de données
ALTER TABLE users MODIFY password VARCHAR(255);
UPDATE users SET password = SHA2('123456', 256) WHERE username = 'admin';
UPDATE users SET password = SHA2('password', 256) WHERE username = 'alice';
Modifier login.php
pour vérifier un mot de passe haché
$query = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$query->execute([$_POST['username']]);
$user = $query->fetch();
if ($user && hash('sha256', $_POST['password']) === $user['password']) {
echo "✔️ Connexion réussie !";
} else {
echo "❌ Échec de la connexion.";
}
?>
Bonnes pratiques
- Utiliser POST au lieu de GET.
- Appliquer les requêtes préparées pour éviter l’injection SQL.
- Échapper les entrées utilisateur avec
htmlspecialchars()
. - Hacher les mots de passe avant de les stocker.