Aller au contenu principal

Bonnes pratiques en dev.

Bonnes pratiques de développement sécurisé : Sécuriser une application contre les failles courantes

Notions théoriques

Développer une application sécurisée est essentiel pour protéger les utilisateurs et leurs données.

De nombreuses attaques exploitent des failles courantes dans le code.

Il est donc important d’adopter des bonnes pratiques dès la conception.

1. Validation et filtrage des entrées utilisateur

Les données saisies par un utilisateur ne doivent jamais être utilisées directement sans contrôle. Une entrée malveillante peut provoquer des comportements inattendus, voire dangereux.

  • Filtrer les entrées : Supprimer ou remplacer les caractères spéciaux pouvant être utilisés dans des attaques.
  • Valider les formats : Vérifier que les données correspondent au type attendu (exemple : une adresse e-mail doit contenir @ et un domaine valide).
  • Limiter la longueur : Restreindre la taille des entrées pour éviter des attaques par dépassement de mémoire.

2. Protection contre l’injection SQL

L’injection SQL est une attaque qui consiste à insérer du code SQL malveillant dans une requête. Pour s’en protéger :

  • Utiliser des requêtes préparées : Séparer les données de la requête SQL.
  • Éviter la concaténation de chaînes : Ne jamais insérer directement des variables utilisateur dans une requête SQL.

3. Sécurisation contre les attaques XSS (Cross-Site Scripting)

Le XSS permet à un attaquant d’exécuter du JavaScript malveillant sur une page Web. Pour éviter cela :

  • Échapper les sorties : Convertir les caractères spéciaux (<, >, &, etc.) en entités HTML.
  • Utiliser une politique de sécurité du contenu (CSP) : Restreindre les scripts autorisés.

4. Gestion des mots de passe

Les mots de passe doivent être protégés pour éviter leur vol en cas de fuite de données :

  • Utiliser un algorithme de hachage sécurisé (bcrypt, argon2).
  • Ne jamais stocker un mot de passe en clair.
  • Appliquer une politique de complexité (longueur minimale, caractères spéciaux, etc.).

5. Protection contre les attaques CSRF (Cross-Site Request Forgery)

Une attaque CSRF force un utilisateur authentifié à effectuer une action non désirée. Pour s’en prémunir :

  • Utiliser des jetons CSRF : Un token unique est généré et vérifié pour chaque requête critique.
  • Limiter la durée de validité des sessions.

Exemple pratique

Il est possible de sécuriser un formulaire d’authentification en appliquant ces bonnes pratiques.

Création du formulaire

Créer un fichier login.html :

<form action="login.php" method="POST">
<input type="text" name="username" placeholder="Nom d'utilisateur" required>
<input type="password" name="password" placeholder="Mot de passe" required>
<input type="submit" value="Se connecter">
</form>

Traitement sécurisé des données (login.php)

<?php
session_start();
$pdo = new PDO('mysql:host=localhost;dbname=securite', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = htmlspecialchars($_POST['username'], ENT_QUOTES, 'UTF-8');
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();

if ($user && password_verify($password, $user['password'])) {
$_SESSION['username'] = $user['username'];
header("Location: dashboard.php");
exit;
} else {
echo "Identifiants incorrects.";
}
}
?>

Test de mémorisation/compréhension


Pourquoi ne faut-il jamais stocker un mot de passe en clair ?


Quelle fonction permet de hacher un mot de passe en PHP ?


Quel est le principal risque d'une injection SQL ?


Quelle méthode permet d'éviter une attaque XSS ?


Quelle est la meilleure pratique pour protéger un formulaire contre le CSRF ?



TP pour réfléchir et résoudre des problèmes

Ce TP a pour objectif de créer un formulaire d'inscription sécurisé en appliquant les bonnes pratiques de développement sécurisé.


Étape 1 : Création de la base de données et de la table des utilisateurs

Avant de créer le formulaire, il est nécessaire d'avoir une base de données pour stocker les utilisateurs.

Instructions :

  1. Ouvrir phpMyAdmin ou un terminal MySQL.
  2. Créer une base de données nommée securite_web :
CREATE DATABASE securite_web;
  1. Sélectionner cette base de données :
USE securite_web;
  1. Créer une table users avec les champs suivants :
    • id : Identifiant unique, auto-incrémenté.
    • username : Nom d'utilisateur unique.
    • password : Mot de passe haché.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
Une solution

Étape 2 : Création du formulaire d'inscription

Un formulaire HTML permet aux utilisateurs de s'inscrire en fournissant un nom d'utilisateur et un mot de passe.

Instructions :

  1. Créer un fichier register.html.
  2. Ajouter le code suivant :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inscription</title>
</head>
<body>
<form action="register.php" method="POST">
<label for="username">Nom d'utilisateur :</label>
<input type="text" name="username" id="username" required minlength="3" maxlength="50">

<label for="password">Mot de passe :</label>
<input type="password" name="password" id="password" required minlength="8">

<input type="submit" value="S'inscrire">
</form>
</body>
</html>
Une solution

Étape 3 : Traitement sécurisé des données

Le fichier register.php doit récupérer les données du formulaire et les stocker de manière sécurisée.

Instructions :

  1. Créer un fichier register.php.
  2. Ajouter le code suivant :
<?php
$pdo = new PDO('mysql:host=localhost;dbname=securite_web', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = trim($_POST['username']);
$password = $_POST['password'];

// Vérification des entrées
if (strlen($username) < 3 || strlen($username) > 50) {
die("Le nom d'utilisateur doit contenir entre 3 et 50 caractères.");
}

if (strlen($password) < 8) {
die("Le mot de passe doit contenir au moins 8 caractères.");
}

// Échapper les entrées pour éviter les attaques XSS
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');

// Hachage du mot de passe
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);

try {
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (:username, :password)");
$stmt->execute([
'username' => $username,
'password' => $hashedPassword
]);
echo "Inscription réussie.";
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
echo "Ce nom d'utilisateur est déjà pris.";
} else {
echo "Erreur lors de l'inscription.";
}
}
}
?>
Une solution

Étape 4 : Vérification de l'inscription

Une fois l'inscription réussie, il est possible de vérifier si un utilisateur peut se connecter.

Instructions :

  1. Créer un fichier login.html.
  2. Ajouter le code suivant :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connexion</title>
</head>
<body>
<form action="login.php" method="POST">
<label for="username">Nom d'utilisateur :</label>
<input type="text" name="username" id="username" required>

<label for="password">Mot de passe :</label>
<input type="password" name="password" id="password" required>

<input type="submit" value="Se connecter">
</form>
</body>
</html>
Une solution

Étape 5 : Traitement de la connexion sécurisée

Créer un fichier login.php :

<?php
session_start();
$pdo = new PDO('mysql:host=localhost;dbname=securite_web', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = htmlspecialchars($_POST['username'], ENT_QUOTES, 'UTF-8');
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();

if ($user && password_verify($password, $user['password'])) {
$_SESSION['username'] = $user['username'];
header("Location: dashboard.php");
exit;
} else {
echo "Identifiants incorrects.";
}
}
?>
Une solution