Vulnérabilités GET et POST
Vulnérabilités liées aux paramètres GET et POST
Notions théoriques
Les paramètres GET et POST sont utilisés pour transmettre des données entre un client (navigateur) et un serveur web.
Une mauvaise gestion des paramètres GET et POST peut entraîner des failles de sécurité exploitables par des attaquants.
Différences entre GET et POST
Critère | GET | POST |
---|---|---|
Visibilité des données | Visible dans l'URL | Caché dans le corps de la requête |
Taille des données | Limité (~2000 caractères) | Illimité |
Utilisation courante | Recherche, navigation | Formulaires, authentification |
Sécurité | Moins sécurisé (exposé dans l'URL) | Plus sécurisé (non stocké dans l'historique) |
Vulnérabilités courantes
Injection SQL
- Un attaquant peut modifier un paramètre GET/POST pour exécuter du SQL malveillant.
- Exemple :
?id=1 OR 1=1
peut permettre d'afficher toutes les données d'une base.
Cross-Site Scripting (XSS)
- L’attaquant injecte du JavaScript malveillant via un paramètre GET/POST.
- Exemple :
?message=<script>alert('XSS')</script>
affiche une alerte piégée.
Falsification de requête (CSRF)
- Un attaquant force un utilisateur à exécuter une action à son insu.
- Exemple : Un formulaire caché POST envoie une requête sans que l’utilisateur le sache.
Exposition de données sensibles
- Les paramètres GET sont stockés dans l’historique du navigateur et peuvent être récupérés.
- Exemple :
?password=123456
peut être visible dans les logs du serveur.
Exemple pratique
Voici un formulaire vulnérable et voyons comment un attaquant peut l'exploiter.
Code d’un formulaire non sécurisé
<!DOCTYPE html>
<html>
<head>
<title>Connexion</title>
</head>
<body>
<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>
Problèmes de sécurité
- Le mot de passe est envoyé en GET, donc visible dans l’URL (
?username=admin&password=123456
). - Un attaquant peut injecter du SQL en testant
?username=admin' --
pour contourner l’authentification. - Pas de validation côté serveur, donc vulnérable aux XSS (
?username=<script>alert('Hacked')</script>
).
Solution sécurisée
- Utiliser POST au lieu de GET.
- Échapper les entrées utilisateur (
htmlspecialchars()
en PHP). - Vérifier les entrées (
preg_match()
pour filtrer les caractères suspects). - Utiliser des requêtes préparées pour éviter l’injection SQL.
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é afin de savoir :
✔️ 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 Debian avec Apache, PHP et MySQL.
1️⃣ Ouvrez un terminal et installez les services nécessaires :
sudo apt update
sudo apt install apache2 php php-mysql mariadb-server -y
2️⃣ Démarrez Apache et MySQL :
sudo systemctl start apache2
sudo systemctl start mariadb
3️⃣ Sécurisez 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).
1️⃣ Connectez-vous à MySQL :
sudo mysql -u root -p
2️⃣ Créez une base de données et une table :
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');
3️⃣ Vérifiez les utilisateurs :
SELECT * FROM users;
Étape 3 : Créer un formulaire vulnérable
Nous allons créer une page de connexion non sécurisée.
1️⃣ Créez un fichier login.php
dans /var/www/html/
:
sudo nano /var/www/html/login.php
2️⃣ Ajoutez 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.";
}
?>
3️⃣ Créez une page HTML pour tester la connexion :
sudo nano /var/www/html/index.html
4️⃣ Ajoutez 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.
➡️ Conclusion : Notre formulaire est vulnérable et doit être sécurisé !
Étape 5 : Sécuriser le formulaire
Nous allons corriger les failles en appliquant les bonnes pratiques.
1️⃣ Ouvrez login.php
et modifiez le code :
sudo nano /var/www/html/login.php
2️⃣ Remplacez 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.";
}
?>
3️⃣ Modifiez le formulaire pour utiliser POST
:
sudo nano /var/www/html/index.html
4️⃣ Corrigez 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
1️⃣ Refaites les tests d’injection SQL :
- Essayez
admin'--
→ Échec de la connexion ✔️ - Essayez
admin" OR 1=1 --
→ Échec de la connexion ✔️
2️⃣ Refaites les tests XSS :
- Essayez
<script>alert('Hacked')</script>
→ Aucun script ne s’exécute ✔️
3️⃣ Vérifiez 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.
1️⃣ Modifiez la création des utilisateurs dans MySQL :
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';
2️⃣ Modifiez 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.";
}