Votre site PHP dans le viseur : pourquoi les hackers aiment la sécurité faible
\n\nImaginez : vous avez passé des semaines à développer un super blog ou une boutique en ligne en PHP. Le site fonctionne, les visiteurs sont satisfaits. Mais un « beau » jour, vous vous connectez à l'admin et il n'y a plus rien. La base de données a été supprimée. Ou pire encore, la page d'accueil affiche une publicité pour un casino et tous les utilisateurs sont redirigés vers un site de phishing.
\n\nUne histoire familière ? Malheureusement, pour de nombreux développeurs PHP débutants (et pas seulement), c'est une réalité. Le plus frustrant, c'est que 90 % de ces attaques sont dues à deux vulnérabilités apparemment simples : les injections SQL et le cross-site scripting (XSS). Les hackers ne cassent pas des algorithmes de chiffrement complexes – ils profitent simplement de l'inattention des programmeurs.
\n\nDans cet article, nous n'allons pas faire de cours ennuyeux. Nous allons comprendre comment ces attaques fonctionnent « de l'intérieur » et, surtout, apprendre à mettre en place des barrières solides. Vous découvrirez comment rendre votre code PHP dur à cuire pour les malfaiteurs. C'est parti !
\n\nInjections SQL : quand la base de données parle avec la voix du hacker
\n\nL'injection SQL (SQL injection) est un type d'attaque où un attaquant « injecte » du code SQL malveillant dans une requête à la base de données. Si votre application ne filtre pas les données saisies, le hacker peut forcer la base de données à exécuter n'importe quelle commande : du vol de mots de passe à la suppression complète des tables.
\n\nÀ quoi ressemble un code vulnérable ?
\n\nImaginez un simple formulaire de connexion. Le code d'un débutant ressemble souvent à ceci :
\n\n<?php\n$login = $_POST['login'];\n$password = $_POST['password'];\n\n// DANGEREUX ! Substitution directe dans la requête\n$query = "SELECT * FROM users WHERE login = '$login' AND password = '$password'";\n$result = mysqli_query($link, $query);\n?>\n\nCela semble logique. Mais que se passe-t-il si l'attaquant saisit la chaîne suivante dans le champ login :
\n\n' OR '1'='1\n\nLa requête devient :
\n\nSELECT * FROM users WHERE login = '' OR '1'='1' AND password = '...'\n\nLa condition '1'='1' est toujours vraie. Félicitations, le hacker vient de se connecter sans mot de passe ! Et s'il saisit '; DROP TABLE users; --, votre table d'utilisateurs disparaîtra pour toujours.
Échappement vs. Requêtes préparées : que choisir ?
\n\nDe nombreux vieux tutoriels recommandent d'utiliser la fonction mysqli_real_escape_string(). Oui, elle échappe les caractères spéciaux, mais c'est dépassé. Cette méthode a des inconvénients :
- \n
- Il est facile d'oublier d'appliquer la fonction à chaque variable. \n
- Elle ne protège pas contre tous les types d'injections (par exemple, lors de l'utilisation de LIKE). \n
- Le code devient sale et difficile à maintenir. \n
La méthode moderne et correcte est d'utiliser les requêtes préparées (prepared statements) via PDO ou MySQLi. Elles séparent la logique de la requête des données. La base de données « compile » d'abord le modèle de la requête, puis y insère les données. Même si le hacker transmet du code malveillant, il sera traité comme une simple chaîne de caractères, et non comme une partie de la commande SQL.
\n\nÉcrire du code sécurisé avec PDO
\n\nVoici à quoi devrait ressembler une requête sécurisée à la base de données en utilisant PDO (PHP Data Objects) :
\n\n<?php\n// Connexion à la base de données\n$dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';\n$pdo = new PDO($dsn, 'user', 'password');\n\n// Configuration du mode d'erreur (IMPORTANT pour le débogage, mais à retirer en production)\n$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n// Préparation de la requête (modèle)\n$stmt = $pdo->prepare("SELECT * FROM users WHERE login = :login AND password = :password");\n\n// Liaison des paramètres et exécution\n$stmt->execute([\n ':login' => $_POST['login'],\n ':password' => $_POST['password']\n]);\n\n$user = $stmt->fetch();\n\nif ($user) {\n echo "Bienvenue !";\n} else {\n echo "Identifiant ou mot de passe incorrect";\n}\n?>\n\nÀ prop