1. Établissez une couche d'abstraction de sécurité
Nous vous déconseillons d'appliquer manuellement les techniques décrites ci-dessus à chaque instance d'entrée utilisateur, mais nous vous recommandons fortement de créer une couche d'abstraction à cet effet. Une abstraction simple consiste à ajouter votre plan de validation à une fonction et à appeler cette fonction pour chaque élément saisi par l'utilisateur. Bien entendu, nous pouvons également créer une abstraction plus complexe, de niveau supérieur, en encapsulant une requête sécurisée dans une classe pouvant être utilisée par toutes les applications. Il existe de nombreux cours gratuits prêts à l'emploi disponibles en ligne ; dans cet article, nous allons en discuter quelques-uns.
Il y a au moins trois avantages à faire cette abstraction (et chacun améliorera le niveau de sécurité) :
1. Code localisé.
2. Rendre la structure de requête plus rapide et plus fiable - car cela peut décharger une partie du travail sur le code abstrait.
3. Lorsqu'il est conçu en tenant compte des fonctionnalités de sécurité et appliqué de manière appropriée, cela empêchera efficacement les différentes attaques par injection dont nous avons parlé plus tôt.
2. Améliorer les applications existantes
Si vous souhaitez améliorer une application existante, il est plus approprié d'utiliser une simple couche d'abstraction. Une fonction qui « nettoie » simplement toute entrée utilisateur que vous collectez pourrait ressembler à ceci :
function safe( $string ) {
return ''' . }
【Remarque】Nous avons construit des guillemets simples correspondant aux requêtes de valeur et à la fonction mysql_real_escape_string(). Ensuite, vous pouvez utiliser cette fonction pour construire une variable $query, comme suit :
$variety = safe( $_POST['variety'] );
$query = ' SELECT * FROM wines WHERE variété =' . $variety;
Maintenant, votre utilisateur tente d'effectuer une attaque par injection - en entrant ce qui suit comme valeur de la variable $variety :
lagrein' ou 1=1;
Remarque que si le 'nettoyage' ci-dessus n'est pas effectué, la requête finale sera la suivante (cela conduira à des résultats imprévisibles) :
SELECT * FROM vins WHERE variété = 'lagrein' ou 1= 1;'
Mais maintenant, puisque la saisie de l'utilisateur a été effacée, l'instruction de requête devient la forme inoffensive suivante :
SELECT * FROM wines WHERE variété = 'lagrein' or 1 =1;'
Si vous créez une nouvelle application, vous pouvez créer une couche d'abstraction de sécurité à partir de zéro. Désormais, la nouvelle prise en charge améliorée de PHP 5 pour MySQL (qui se reflète principalement dans la nouvelle extension mysqli) offre une prise en charge solide de cette fonctionnalité de sécurité (à la fois procédurale et orientée objet). Vous pouvez obtenir des informations sur mysqli sur le site http://php.net/mysqli. Notez que ce support mysqli n'est disponible que si vous compilez PHP avec l'option --with-mysqli=path/to/mysql_config. Voici une version procédurale de ce code, utilisée pour sécuriser une requête basée sur mysqli :
<?php
//Récupérer les entrées de l'utilisateur
$animalName = $_POST['animalName'];
//Se connecter à la base de données
$connect = mysqli_connect( ' localhost ', 'username', 'password', 'database' );
if ( !$connect ) exit( 'connection failed: ' . mysqli_connect_error() );
//Créer une source d'instruction de requête
$stmt = mysqli_prepare( $connect,'SELECT intelligence FROM animaux WHERE name = ?' );
if ( $stmt ) {
// Lier l'échange à la déclaration
mysqli_stmt_bind_param( $stmt, 's ', $animalName );
//Exécuter l'instruction
mysqli_stmt_execute( $stmt );
//Récupération des résultats...
mysqli_stmt_bind_result( $stmt, $intelligence ); et affichez-le
if ( mysqli_stmt_fetch( $stmt ) ) {
print 'A $animalName has $intelligence intelligence.n';
} else {
print 'Désolé, aucun enregistrement trouvé.';
}
//Effacer la source de l'instruction
mysqli_stmt_close( $stmt );
}
mysqli_close( $connect );
?>
L'extension mysqli Un ensemble de fonctions est prévu pour structurer et exécuter les requêtes. De plus, il fournit également de manière très précise les fonctionnalités obtenues précédemment en utilisant notre propre fonction safe().
Dans l'extrait ci-dessus, le contenu d'entrée soumis par l'utilisateur est d'abord collecté et la connexion à la base de données est établie. Ensuite, utilisez la fonction mysqli_prepare() pour créer une source de requête - ici nommée $stmt pour refléter le nom de la fonction qui l'utilise. Cette fonction prend deux paramètres : la ressource de connexion et une chaîne (à chaque fois que vous utilisez l'expansion pour insérer une valeur, la marque '?' y est insérée). Dans ce cas, vous n’avez qu’une seule valeur : le nom de l’animal.
Notez que dans une instruction SELECT, le seul endroit valide pour placer la marque '?' est dans la partie comparaison de valeurs. C'est exactement pourquoi vous n'avez pas besoin de spécifier quelle variable appliquer (sauf dans la fonction mysqli_stmt_bind_param()). Ici, vous devez également spécifier son type – dans ce cas, « s » signifie chaîne. Les autres types possibles sont : « I » pour un entier, « d » pour un double (ou float) et « b » pour une chaîne binaire.
Les fonctions mysqli_stmt_execute(), mysqli_stmt_bind_result() et mysqli_stmt_fetch() sont responsables de l'exécution des requêtes et de la récupération des résultats. Si des résultats de recherche existent, affichez-les ; si aucun résultat n’existe, affichez un message inoffensif. Enfin, vous devez fermer la ressource $stmt et la connexion à la base de données – libérez-les de la mémoire.
En supposant qu'un utilisateur légitime saisit la chaîne « lemming », alors cette routine (en supposant que les données appropriées dans la base de données) génèrent le message « Un lemming a une intelligence très faible ». En supposant qu'il y ait une tentative d'injection - par exemple 'lemming' ou 1=1;', alors cette routine affichera le message (inoffensif) 'Désolé, aucun enregistrement trouvé.'.
De plus, l'extension mysqli fournit également une version orientée objet de la même routine. Ci-dessous, nous aimerions clarifier comment cette version est appliquée.
<?php
$animalName = $_POST['animalName'];
$mysqli = new mysqli( 'localhost', 'username', 'password', 'database');
if ( !$mysqli ) exit( 'connection failed: ' . mysqli_connect_error() );
$stmt = $mysqli->prepare( 'SELECT intelligence
if ( $stmt ) {
$stmt->bind_param( 's', $animalName );
$stmt->execute();
$stmt->bind_result( $intelligence );
if ( $stmt->fetch() ) {
print 'Un $animalName a $intelligence intelligence.n';
} else {
print 'Désolé, aucun enregistrement trouvé . '; Copie du code - il applique une approche de syntaxe et d'organisation orientée objet plutôt qu'un code strictement procédural.
4. Abstraction de niveau supérieur
Si vous utilisez la bibliothèque externe PearDB, vous pouvez alors entièrement abstraire le module de sécurité de l'application.
D'un autre côté, il y a un inconvénient majeur à utiliser cette bibliothèque : on ne peut être limité que par les idées de certaines personnes, et beaucoup de travail a été ajouté à la gestion du code. Pour cette raison, vous devez bien réfléchir avant de décider de les appliquer ou non. Si vous décidez de faire cela, assurez-vous au moins qu'ils vous aident réellement à « nettoyer » les entrées de vos utilisateurs.
5. Testez vos capacités de protection contre les injections
Comme nous en avons discuté plus tôt, une partie importante pour garantir la sécurité de vos scripts consiste à les tester. La meilleure façon d’y parvenir est de créer vous-même des tests d’injection de code SQL.
Nous fournissons ici un exemple d'un tel test. Dans cet exemple, nous testons une attaque par injection sur une instruction SELECT.
<?php
//Fonction de protection testée
function safe( $string ) {
return ''' . >//Se connecter à la base de données
//////////////////////
//Tentative d'injection
//// // ////////////////
$exploit = 'lemming' AND 1=1;';
//Liquidate
$safe = safe( $ exploit );
$query = 'SELECT * FROM animaux WHERE name = $safe';
$result = mysql_query( $query );
//Tester si la protection est suffisante
if ( $ result && mysql_num_rows( $result ) == 1 ) {
exit 'Protection réussie :n
exploit $exploit a été neutralisé.';
}
else {
exit( 'La protection a échoué : n
exploit $exploit a pu récupérer toutes les lignes.' );
}
?>
Si vous souhaitez créer un tel ensemble de tests et expérimenter diverses commandes SQL basées sur différentes injections , vous détecterez rapidement toute faille dans votre stratégie de protection. Une fois que vous aurez corrigé ces en-têtes, vous pouvez être sûr d’avoir établi une véritable protection contre les attaques par injection.
6. Résumé
Au début de cette série d'articles, nous avons analysé une menace spécifique pesant sur vos scripts via une discussion sur l'injection SQL - provoquée par une entrée inappropriée de l'utilisateur. Après cela, nous avons décrit le fonctionnement de l’injection SQL et analysé exactement comment PHP est facilement injecté. Nous fournissons ensuite un exemple d’injection réel. Ensuite, nous recommandons une série de mesures pour rendre inoffensive une tentative d'attaque par injection - ce serait en garantissant que toutes les valeurs soumises sont entourées de guillemets, en vérifiant le type de valeurs soumises par l'utilisateur et en filtrant votre entrée utilisateur. est réalisé grâce à des méthodes avancées telles que la prise en embuscade de personnages dangereux. Enfin, nous vous recommandons d'abstraire vos routines de validation et de fournir des exemples de scripts pour modifier une application existante. Nous avons ensuite discuté des avantages et des inconvénients des solutions d'abstraction tierces.
Ce qui précède est le troisième contenu interdisant complètement les attaques par injection SQL en PHP. Pour plus de contenu connexe, veuillez faire attention au site Web chinois de PHP (www.php.cn) !