Question

Si une entrée utilisateur est insérée sans modification dans une requête SQL, l'application devient alors vulnérable aux Injection SQL, comme dans l'exemple suivant :

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

C'est parce que l'utilisateur peut saisir quelque chose comme value'); DROP TABLE table;--, et la requête devient :

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Que peut-on faire pour éviter que cela ne se produise ?

Était-ce utile?

La solution

Utilisez des instructions préparées et des requêtes paramétrées. Il s'agit d'instructions SQL envoyées et analysées par le serveur de base de données séparément de tout paramètre.De cette façon, il est impossible pour un attaquant d’injecter du SQL malveillant.

Vous avez essentiellement deux options pour y parvenir :

  1. En utilisant AOP (pour tout pilote de base de données pris en charge) :

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. En utilisant MySQLi (pour MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

Si vous vous connectez à une base de données autre que MySQL, il existe une deuxième option spécifique au pilote à laquelle vous pouvez vous référer (par ex. pg_prepare() et pg_execute() pour PostgreSQL).Le PDO est l’option universelle.

Configurer correctement la connexion

Notez que lors de l'utilisation PDO pour accéder à une base de données MySQL réel les déclarations préparées sont non utilisé par défaut.Pour résoudre ce problème, vous devez désactiver l'émulation des instructions préparées.Un exemple de création d'une connexion à l'aide de PDO est :

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Dans l'exemple ci-dessus, le mode erreur n'est pas strictement nécessaire, mais il est conseillé de l'ajouter.De cette façon, le script ne s'arrêtera pas avec un Fatal Error quand quelque chose ne va pas.Et cela donne au développeur la possibilité de catch toute erreur thrown comme PDOExceptions.

Qu'est-ce que obligatoire, cependant, est le premier setAttribute() ligne, qui indique à PDO de désactiver les instructions préparées émulées et d'utiliser réel déclarations préparées.Cela garantit que l'instruction et les valeurs ne sont pas analysées par PHP avant de l'envoyer au serveur MySQL (ne donnant aucune chance à un éventuel attaquant d'injecter du SQL malveillant).

Bien que vous puissiez définir le charset dans les options du constructeur, il est important de noter que les versions 'anciennes' de PHP (< ​​5.3.6) ignoré silencieusement le paramètre charset dans la DSN.

Explication

Ce qui se passe, c'est que l'instruction SQL à laquelle vous transmettez prepare est analysé et compilé par le serveur de base de données.En spécifiant des paramètres (soit un ? ou un paramètre nommé comme :name dans l'exemple ci-dessus), vous indiquez au moteur de base de données sur quoi vous souhaitez filtrer.Puis quand tu appelles execute, l'instruction préparée est combinée avec les valeurs de paramètre que vous spécifiez.

L'important ici est que les valeurs des paramètres soient combinées avec l'instruction compilée, et non avec une chaîne SQL.L'injection SQL fonctionne en incitant le script à inclure des chaînes malveillantes lorsqu'il crée du SQL à envoyer à la base de données.Ainsi, en envoyant le SQL réel séparément des paramètres, vous limitez le risque de vous retrouver avec quelque chose que vous n'aviez pas prévu.Tous les paramètres que vous envoyez lors de l'utilisation d'une instruction préparée seront simplement traités comme des chaînes (bien que le moteur de base de données puisse effectuer certaines optimisations afin que les paramètres puissent également se retrouver sous forme de nombres, bien sûr).Dans l'exemple ci-dessus, si le $name la variable contient 'Sarah'; DELETE FROM employees le résultat serait simplement une recherche de la chaîne "'Sarah'; DELETE FROM employees", et tu ne finiras pas avec une table vide.

Un autre avantage de l'utilisation d'instructions préparées est que si vous exécutez la même instruction plusieurs fois au cours de la même session, elle ne sera analysée et compilée qu'une seule fois, ce qui vous permettra de gagner en vitesse.

Oh, et puisque vous avez demandé comment procéder pour un insert, voici un exemple (en utilisant PDO) :

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

Les instructions préparées peuvent-elles être utilisées pour les requêtes dynamiques ?

Bien que vous puissiez toujours utiliser des instructions préparées pour les paramètres de requête, la structure de la requête dynamique elle-même ne peut pas être paramétrée et certaines fonctionnalités de la requête ne peuvent pas être paramétrées.

Pour ces scénarios spécifiques, la meilleure chose à faire est d'utiliser un filtre de liste blanche qui restreint les valeurs possibles.

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

Autres conseils

Avertissement obsolète :L'exemple de code de cette réponse (comme l'exemple de code de la question) utilise PHP MySQL extension, qui était obsolète dans PHP 5.5.0 et entièrement supprimée dans PHP 7.0.0.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

Si vous utilisez une version récente de PHP, le mysql_real_escape_string l'option décrite ci-dessous ne sera plus disponible (bien que mysqli::escape_string est un équivalent moderne).Ces jours-ci, le mysql_real_escape_string cette option n’aurait de sens que pour le code existant sur une ancienne version de PHP.


Vous avez deux options : échapper aux caractères spéciaux de votre unsafe_variable, ou en utilisant une requête paramétrée.Les deux vous protégeraient de l’injection SQL.La requête paramétrée est considérée comme la meilleure pratique, mais nécessitera le passage à une extension MySQL plus récente en PHP avant de pouvoir l'utiliser.

Nous couvrirons d'abord la chaîne à faible impact qui en échappe à une.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Voir également les détails de mysql_real_escape_string fonction.

Pour utiliser la requête paramétrée, vous devez utiliser MySQLi plûtot que le MySQL les fonctions.Pour réécrire votre exemple, nous aurions besoin de quelque chose comme ce qui suit.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

La fonction clé que vous voudrez lire là-bas serait mysqli::prepare.

De plus, comme d'autres l'ont suggéré, vous trouverez peut-être utile/plus facile d'intensifier une couche d'abstraction avec quelque chose comme AOP.

Veuillez noter que le cas sur lequel vous avez posé une question est assez simple et que des cas plus complexes peuvent nécessiter des approches plus complexes.En particulier:

  • Si vous souhaitez modifier la structure du SQL en fonction des entrées de l'utilisateur, les requêtes paramétrées ne seront d'aucune utilité et l'échappement requis n'est pas couvert par mysql_real_escape_string.Dans ce genre de cas, il serait préférable de transmettre les entrées de l'utilisateur via une liste blanche pour garantir que seules les valeurs « sûres » sont autorisées.
  • Si vous utilisez des entiers provenant de la saisie utilisateur dans une condition et prenez le mysql_real_escape_string approche, vous souffrirez du problème décrit par Polynôme dans les commentaires ci-dessous.Ce cas est plus délicat car les entiers ne seraient pas entourés de guillemets, vous pouvez donc le résoudre en validant que la saisie utilisateur ne contient que des chiffres.
  • Il existe probablement d'autres cas dont je ne suis pas au courant.Vous pourriez trouver ce est une ressource utile sur certains des problèmes les plus subtils que vous pouvez rencontrer.

Chaque réponse ici ne couvre qu'une partie du problème.En fait, il y a quatre différentes parties de requête que nous pouvons y ajouter dynamiquement :-

  • un string
  • un numéro
  • un identifiant
  • un mot-clé de syntaxe.

Et les déclarations préparées n’en couvrent que deux.

Mais parfois, nous devons rendre notre requête encore plus dynamique, en ajoutant également des opérateurs ou des identifiants.Nous aurons donc besoin de différentes techniques de protection.

En général, une telle approche de protection repose sur liste blanche.

Dans ce cas, chaque paramètre dynamique doit être codé en dur dans votre script et choisi parmi cet ensemble.Par exemple, pour effectuer un tri dynamique :

$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Cependant, il existe un autre moyen de sécuriser les identifiants : la fuite.Tant que vous avez un identifiant cité, vous pouvez échapper aux backticks à l'intérieur en les doublant.

Comme étape supplémentaire, nous pouvons emprunter une idée vraiment brillante consistant à utiliser un espace réservé (un proxy pour représenter la valeur réelle dans la requête) à partir des instructions préparées et inventer un espace réservé d'un autre type - un espace réservé d'identification.

Donc, pour faire court :c'est un espace réservé, pas Affirmation préparée peut être considéré comme une solution miracle.

Ainsi, une recommandation générale peut être formulée ainsiTant que vous ajoutez des parties dynamiques à la requête à l'aide d'espaces réservés (et que ces espaces réservés sont correctement traités bien sûr), vous pouvez être sûr que votre requête est sécurisée..

Il existe néanmoins un problème avec les mots-clés de syntaxe SQL (tels que AND, DESC et autres), mais la liste blanche semble être la seule approche dans ce cas.

Mise à jour

Bien qu'il existe un accord général sur les meilleures pratiques en matière de protection contre les injections SQL, il existe encore beaucoup de mauvaises pratiques également. Et certains d’entre eux sont trop profondément ancrés dans l’esprit des utilisateurs de PHP.Par exemple, sur cette page même, il y a (bien qu'invisibles pour la plupart des visiteurs) plus de 80 réponses supprimées - tous supprimés par la communauté en raison de leur mauvaise qualité ou de la promotion de mauvaises pratiques obsolètes.Pire encore, certaines des mauvaises réponses ne sont pas supprimées, mais prospèrent.

Par exemple, là(1) sont(2) encore(3) beaucoup(4) réponses(5), incluant le deuxième réponse la plus votée vous suggérant d'échapper manuellement la chaîne - une approche obsolète qui s'est avérée non sécurisée.

Ou il existe une réponse légèrement meilleure qui suggère simplement une autre méthode de formatage de chaîne et le présente même comme la panacée ultime.Bien sûr, ce n’est pas le cas.Cette méthode n'est pas meilleure que le formatage de chaîne classique, mais elle conserve tous ses inconvénients :il s'applique uniquement aux chaînes et, comme tout autre formatage manuel, il s'agit essentiellement d'une mesure facultative et non obligatoire, sujette à des erreurs humaines de toute sorte.

Je pense que tout cela est dû à une très vieille superstition, soutenue par des autorités telles que OWASP ou le manuel PHP, qui proclame l'égalité entre tout ce qui "s'échappe" et la protection contre les injections SQL.

Indépendamment de ce que dit le manuel PHP depuis des lustres, *_escape_string ne sécurise en aucun cas les données et cela n’a jamais été prévu.En plus d'être inutile pour toute partie SQL autre que la chaîne, l'échappement manuel est erroné, car il est manuel plutôt qu'automatisé.

Et l'OWASP ne fait qu'empirer les choses, en mettant l'accent sur la fuite. entrée de l'utilisateur ce qui est complètement absurde :de tels mots ne devraient pas exister dans le contexte de la protection contre les injections.Chaque variable est potentiellement dangereuse, quelle qu’en soit la source !Ou, en d'autres termes, chaque variable doit être correctement formatée pour être insérée dans une requête, quelle que soit la source.C'est la destination qui compte.Dès qu'un développeur commence à séparer les moutons des chèvres (en se demandant si une variable particulière est « sûre » ou non), il fait son premier pas vers le désastre.Sans oublier que même le libellé suggère une évasion massive au point d'entrée, ressemblant à la fonction de citations très magique - déjà méprisée, obsolète et supprimée.

Ainsi, contrairement à tout ce qui "s'échappe", les déclarations préparées est la mesure qui protège effectivement de l’injection SQL (le cas échéant).

Si vous n'êtes toujours pas convaincu, voici une explication étape par étape que j'ai écrite : Le guide de l'auto-stoppeur sur la prévention des injections SQL, où j'ai expliqué toutes ces questions en détail et j'ai même compilé une section entièrement dédiée aux mauvaises pratiques et à leur divulgation.

Je recommanderais d'utiliser AOP (PHP Data Objects) pour exécuter des requêtes SQL paramétrées.

Non seulement cela protège contre l’injection SQL, mais cela accélère également les requêtes.

Et en utilisant PDO plutôt que mysql_, mysqli_, et pgsql_ fonctions, vous rendez votre application un peu plus abstraite de la base de données, dans les rares cas où vous devez changer de fournisseur de base de données.

Utiliser PDO et des requêtes préparées.

($conn est un PDO objet)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

Comme vous pouvez le constater, les gens vous suggèrent d'utiliser au maximum les instructions préparées.Ce n'est pas faux, mais lorsque votre requête est exécutée juste une fois par processus, il y aurait une légère pénalité de performances.

J'étais confronté à ce problème, mais je pense l'avoir résolu en très manière sophistiquée - la façon dont les pirates utilisent pour éviter d'utiliser des guillemets.Je l'ai utilisé conjointement avec des instructions préparées émulées.Je l'utilise pour prévenir tous types d'attaques possibles par injection SQL.

Mon approche:

  • Si vous vous attendez à ce que l'entrée soit un nombre entier, assurez-vous qu'elle est vraiment entier.Dans un langage de type variable comme PHP, c'est ceci très important.Vous pouvez utiliser par exemple cette solution très simple mais puissante : sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Si vous attendez autre chose d'entier sortilège.Si vous le lancez un sortilège, vous échapperez parfaitement à toute entrée.En C/C++, il existe une fonction appelée mysql_hex_string(), en PHP, vous pouvez utiliser bin2hex().

    Ne vous inquiétez pas, la chaîne échappée aura une taille 2x de sa longueur d'origine car même si vous utilisez mysql_real_escape_string, PHP doit allouer la même capacité ((2*input_length)+1), ce qui est pareil.

  • Cette méthode hexadécimale est souvent utilisée lorsque vous transférez des données binaires, mais je ne vois aucune raison de ne pas l'utiliser sur toutes les données pour empêcher les attaques par injection SQL.Notez que vous devez ajouter les données avec 0x ou utilisez la fonction MySQL UNHEX plutôt.

Ainsi, par exemple, la requête :

SELECT password FROM users WHERE name = 'root'

Va devenir:

SELECT password FROM users WHERE name = 0x726f6f74

ou

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex est l'évasion parfaite.Pas moyen de s'injecter.

Différence entre la fonction UNHEX et le préfixe 0x

Il y a eu quelques discussions dans les commentaires, donc je veux enfin que ce soit clair.Ces deux approches sont très similaires, mais elles sont un peu différentes sur certains points :

Le préfixe ** 0x** ne peut être utilisé que pour les colonnes de données telles que char, varchar, texte, bloc, binaire, etc..
De plus, son utilisation est un peu compliquée si vous êtes sur le point d'insérer une chaîne vide.Vous devrez le remplacer entièrement par '', ou vous obtiendrez une erreur.

UNHEX() travaille sur n'importe lequel colonne;vous n'avez pas à vous soucier de la chaîne vide.


Les méthodes hexadécimales sont souvent utilisées comme attaques

Notez que cette méthode hexadécimale est souvent utilisée comme attaque par injection SQL où les entiers sont comme des chaînes et s'échappent simplement avec mysql_real_escape_string.Vous pouvez alors éviter l’utilisation de guillemets.

Par exemple, si vous faites quelque chose comme ceci :

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

une attaque peut vous injecter beaucoup facilement.Considérez le code injecté suivant renvoyé par votre script :

SÉLECTIONNER ...OÙ identifiant = -1 union sélectionne tous table_name dans information_schema.tables

et maintenant, extrayez simplement la structure de la table :

SÉLECTIONNER ...OÙ id = -1 union all select column_name from information_schema.column où table_name = 0x61727469636c65

Et puis sélectionnez simplement les données que vous souhaitez.N'est-ce pas cool ?

Mais si le codeur d’un site injectable le faisait, aucune injection ne serait possible car la requête ressemblerait à ceci : SELECT ... WHERE id = UNHEX('2d312075...3635')

Avertissement obsolète :L'exemple de code de cette réponse (comme l'exemple de code de la question) utilise PHP MySQL extension, qui était obsolète dans PHP 5.5.0 et entièrement supprimée dans PHP 7.0.0.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

IMPORTANT

La meilleure façon d'empêcher l'injection SQL est d'utiliser Déclarations préparées au lieu de s'échapper, comme la réponse acceptée démontre.

Il existe des bibliothèques telles que Aura.Sql et FacileDB qui permettent aux développeurs d'utiliser plus facilement les instructions préparées.Pour en savoir plus sur les raisons pour lesquelles les déclarations préparées sont meilleures arrêter l'injection SQL, faire référence à ce mysql_real_escape_string() contourne et récemment corrigé les vulnérabilités d'injection SQL Unicode dans WordPress.

Prévention des injections - mysql_real_escape_string()

PHP dispose d'une fonction spécialement conçue pour empêcher ces attaques.Tout ce que vous avez à faire est d'utiliser la bouchée d'une fonction, mysql_real_escape_string.

mysql_real_escape_string prend une chaîne qui va être utilisée dans une requête MySQL et renvoie la même chaîne avec toutes les tentatives d'injection SQL échappées en toute sécurité.Fondamentalement, il remplacera les guillemets gênants (') qu'un utilisateur pourrait saisir par un substitut sécurisé pour MySQL, un guillemet d'échappement \'.

NOTE: vous devez être connecté à la base de données pour utiliser cette fonction !

// Connectez-vous à MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

Vous pouvez trouver plus de détails dans MySQL - Prévention des injections SQL.

Avertissement obsolète :L'exemple de code de cette réponse (comme l'exemple de code de la question) utilise PHP MySQL extension, qui était obsolète dans PHP 5.5.0 et entièrement supprimée dans PHP 7.0.0.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

Vous pourriez faire quelque chose de basique comme ceci :

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Cela ne résoudra pas tous les problèmes, mais c’est un très bon tremplin.J'ai omis des éléments évidents comme la vérification de l'existence de la variable, son format (chiffres, lettres, etc.).

Quoi que vous utilisiez, assurez-vous de vérifier que votre entrée n'a pas déjà été altérée par magic_quotes ou d'autres détritus bien intentionnés, et si nécessaire, parcourez-les stripslashes ou quoi que ce soit pour le désinfecter.

Avertissement obsolète :L'exemple de code de cette réponse (comme l'exemple de code de la question) utilise PHP MySQL extension, qui était obsolète dans PHP 5.5.0 et entièrement supprimée dans PHP 7.0.0.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

La requête paramétrée ET la validation des entrées sont la voie à suivre.Il existe de nombreux scénarios dans lesquels une injection SQL peut se produire, même si mysql_real_escape_string() a été utilisé.

Ces exemples sont vulnérables à l'injection SQL :

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

ou

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

Dans les deux cas, vous ne pouvez pas utiliser ' pour protéger l'encapsulation.

Source: L'injection SQL inattendue (lorsque l'échappement ne suffit pas)

À mon avis, la meilleure façon d'empêcher généralement l'injection SQL dans votre application PHP (ou toute application Web, d'ailleurs) est de réfléchir à l'architecture de votre application.Si le seul moyen de vous protéger contre l'injection SQL est de vous rappeler d'utiliser une méthode ou une fonction spéciale qui fait la bonne chose à chaque fois que vous parlez à la base de données, vous vous trompez.De cette façon, ce n'est qu'une question de temps avant que vous oubliiez de formater correctement votre requête à un moment donné dans votre code.

Adopter le modèle MVC et un framework comme GâteauPHP ou CodeIgniter est probablement la bonne voie à suivre :Les tâches courantes telles que la création de requêtes de base de données sécurisées ont été résolues et mises en œuvre de manière centralisée dans de tels cadres.Ils vous aident à organiser votre application Web de manière judicieuse et vous font réfléchir davantage au chargement et à la sauvegarde d'objets qu'à la construction sécurisée de requêtes SQL uniques.

je favorise procédures stockées (MySQL prend en charge les procédures stockées depuis la version 5.0) d'un point de vue sécurité - les avantages sont -

  1. La plupart des bases de données (y compris MySQL) permettent de limiter l'accès des utilisateurs à l'exécution de procédures stockées.Le contrôle d’accès de sécurité précis est utile pour empêcher les attaques par élévation de privilèges.Cela empêche les applications compromises de pouvoir exécuter SQL directement sur la base de données.
  2. Ils extraient la requête SQL brute de l'application, de sorte que moins d'informations sur la structure de la base de données sont disponibles pour l'application.Il est donc plus difficile pour les utilisateurs de comprendre la structure sous-jacente de la base de données et de concevoir des attaques appropriées.
  3. Elles n’acceptent que des paramètres, les avantages des requêtes paramétrées sont donc là.Bien sûr - IMO, vous devez toujours nettoyer votre entrée - surtout si vous utilisez du SQL dynamique dans la procédure stockée.

Les inconvénients sont -

  1. Elles (procédures stockées) sont difficiles à maintenir et ont tendance à se multiplier très rapidement.Cela rend leur gestion un problème.
  2. Ils ne sont pas très adaptés aux requêtes dynamiques - s'ils sont conçus pour accepter du code dynamique comme paramètres, de nombreux avantages sont annulés.

Il existe de nombreuses façons d'empêcher les injections SQL et autres hacks SQL.Vous pouvez facilement le trouver sur Internet (Recherche Google).Bien sûr Le PDO est une des bonnes solutions. Mais je voudrais vous suggérer quelques bonnes préventions de liens provenant de l'injection SQL.

Qu'est-ce que l'injection SQL et comment l'éviter

Manuel PHP pour l'injection SQL

Explication Microsoft de l'injection SQL et de la prévention en PHP

et d'autres comme Prévenir l'injection SQL avec MySQL et PHP

Maintenant, pourquoi devez-vous empêcher votre requête d'être injectée SQL ?

Je voudrais vous faire savoir :Pourquoi essayons-nous d'empêcher l'injection SQL avec un court exemple ci-dessous :

Requête de correspondance d'authentification de connexion :

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Maintenant, si quelqu'un (un hacker) met

$_POST['email']= admin@emali.com' OR '1=1

et mot de passe n'importe quoi....

La requête sera analysée dans le système uniquement jusqu'à :

$query="select * from users where email='admin@emali.com' OR '1=1';

L'autre partie sera rejetée.Alors, que va-t-il se passer ?Un utilisateur non autorisé (hacker) pourra se connecter en tant qu'administrateur sans avoir son mot de passe.Désormais, il peut faire tout ce que l'administrateur/e-mail peut faire.Vous voyez, c'est très dangereux si l'injection SQL n'est pas empêchée.

Je pense que si quelqu'un veut utiliser PHP et MySQL ou un autre serveur de base de données :

  1. Pensez à apprendre AOP (PHP Data Objects) – il s’agit d’une couche d’accès à la base de données fournissant une méthode uniforme d’accès à plusieurs bases de données.
  2. Pensez à apprendre MySQLi
  3. Utilisez des fonctions PHP natives comme : strip_tags, mysql_real_escape_string ou si variable numérique, juste (int)$foo.En savoir plus sur le type de variables en PHP ici.Si vous utilisez des bibliothèques telles que PDO ou MySQLi, utilisez toujours PDO::quote() et mysqli_real_escape_string().

Exemples de bibliothèques :

---- AOP

----- Aucun espace réservé - prêt pour l'injection SQL ! C'est mauvais

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- Espaces réservés sans nom

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- Espaces réservés nommés

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

P.S.:

PDO remporte cette bataille avec facilité.Avec la prise en charge de douze pilotes de base de données différents et des paramètres nommés, nous pouvons ignorer la petite perte de performances et nous habituer à son API.Du point de vue de la sécurité, les deux sont en sécurité tant que le développeur les utilise comme ils sont censés être utilisés

Mais bien que l'APD et MySQLI soient assez rapides, MySQLI fonctionne de manière insignifiante plus rapidement dans les repères - ~ 2,5% pour les instructions non préparées et ~ 6,5% pour les préparations.

Et veuillez tester chaque requête sur votre base de données - c'est un meilleur moyen d'empêcher l'injection.

Si possible, transtypez les types de vos paramètres.Mais cela ne fonctionne que sur des types simples comme int, bool et float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Si vous souhaitez profiter des moteurs de cache, comme Rédis ou Memcaché, peut-être que DALMP pourrait être un choix.Il utilise du pur MySQLi.Vérifie ça: Couche d'abstraction de base de données DALMP pour MySQL utilisant PHP.

En outre, vous pouvez « préparer » vos arguments avant de préparer votre requête afin de pouvoir créer des requêtes dynamiques et, à la fin, disposer d'une requête d'instructions entièrement préparée. Couche d'abstraction de base de données DALMP pour MySQL utilisant PHP.

Pour ceux qui ne savent pas comment utiliser le PDO (venant du mysql_ fonctions), j'ai fait un wrapper PDO très, très simple c'est un seul fichier.Il existe pour montrer à quel point il est facile de réaliser toutes les tâches courantes que les applications doivent effectuer.Fonctionne avec PostgreSQL, MySQL et SQLite.

En gros, lis-le pendant que tu lis le manuel pour voir comment utiliser les fonctions PDO dans la vie réelle pour simplifier le stockage et la récupération des valeurs au format toi vouloir.

Je veux une seule colonne

$count = DB::column('SELECT COUNT(*) FROM `user`);

Je veux un résultat de tableau (clé => valeur) (c'est-à-direpour faire une boîte de sélection)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

Je veux un résultat sur une seule ligne

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

Je veux un tableau de résultats

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

Utiliser cette fonction PHP mysql_escape_string() vous pouvez obtenir une bonne prévention de manière rapide.

Par exemple:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string — Échappe une chaîne à utiliser dans une requête mysql_query

Pour plus de prévention, vous pouvez ajouter à la fin...

wHERE 1=1   or  LIMIT 1

Au final vous obtenez :

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

Quelques directives pour échapper les caractères spéciaux dans les instructions SQL.

Ne pas utiliser MySQL, cette extension est obsolète, utilisez MySQLi ou AOP.

MySQLi

Pour échapper manuellement les caractères spéciaux dans une chaîne, vous pouvez utiliser le mysqli_real_escape_string fonction.La fonction ne fonctionnera pas correctement à moins que le jeu de caractères correct soit défini avec mysqli_set_charset.

Exemple:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

Pour l'échappement automatique des valeurs avec des instructions préparées, utilisez mysqli_prepare, et mysqli_stmt_bind_param où les types des variables de liaison correspondantes doivent être fournis pour une conversion appropriée :

Exemple:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

Peu importe si vous utilisez des instructions préparées ou mysqli_real_escape_string, vous devez toujours connaître le type de données d'entrée avec lequel vous travaillez.

Donc, si vous utilisez une instruction préparée, vous devez spécifier les types de variables pour la fonction mysqli_stmt_bind_param.

Et l'utilisation de mysqli_real_escape_string sert, comme son nom l'indique, à échapper des caractères spéciaux dans une chaîne, donc cela ne sécurisera pas les entiers.Le but de cette fonction est d'éviter la rupture des chaînes dans les instructions SQL et les dommages à la base de données que cela pourrait causer.mysqli_real_escape_string est une fonction utile lorsqu'elle est utilisée correctement, notamment lorsqu'elle est combinée avec sprintf.

Exemple:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

L'alternative simple à ce problème pourrait être résolue en accordant les autorisations appropriées dans la base de données elle-même.Par exemple:si vous utilisez une base de données MySQL, entrez dans la base de données via le terminal ou l'interface utilisateur fournie et suivez simplement cette commande :

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

Cela limitera l'utilisateur à se limiter uniquement aux requêtes spécifiées.Supprimez l'autorisation de suppression afin que les données ne soient jamais supprimées de la requête lancée depuis la page PHP.La deuxième chose à faire est de vider les privilèges afin que MySQL actualise les autorisations et les mises à jour.

FLUSH PRIVILEGES; 

plus d'informations sur affleurer.

Pour voir les privilèges actuels de l’utilisateur, lancez la requête suivante.

select * from mysql.user where User='username';

En savoir plus sur ACCORDER.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

Avertissement obsolète:L'extension mysql est obsolète pour le moment.nous vous recommandons d'utiliser le Extension AOP

J'utilise trois manières différentes pour empêcher mon application Web d'être vulnérable à l'injection SQL.

  1. Utilisation de mysql_real_escape_string(), qui est une fonction prédéfinie dans PHP, et ce code ajoute des barres obliques inverses aux caractères suivants : \x00, \n, \r, \, ', " et \x1a.Transmettez les valeurs d'entrée en tant que paramètres pour minimiser les risques d'injection SQL.
  2. La méthode la plus avancée consiste à utiliser des PDO.

J'espère que cela t'aidera.

Considérez la requête suivante :

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string() ne protégera pas ici.Si vous utilisez des guillemets simples (' ') autour de vos variables dans votre requête, c'est ce qui vous protège contre cela.Voici une solution ci-dessous pour cela :

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

Ce question a de bonnes réponses à ce sujet.

Je suggère que l'utilisation de PDO soit la meilleure option.

Modifier:

mysql_real_escape_string() est obsolète depuis PHP 5.5.0.Utilisez soit mysqli, soit PDO.

Une alternative à mysql_real_escape_string() est

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Exemple:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

Concernant les nombreuses réponses utiles, j'espère ajouter quelques valeurs à ce fil.L'injection SQL est une attaque qui peut être effectuée via des entrées utilisateur (entrées remplies par l'utilisateur puis utilisées dans des requêtes). Les modèles d'injection SQL sont une syntaxe de requête correcte alors que nous pouvons l'appeler :mauvaises requêtes pour de mauvaises raisons, nous supposons qu'il pourrait y avoir une mauvaise personne qui tente d'obtenir des informations secrètes (en contournant le contrôle d'accès) qui affectent les trois principes de sécurité (Confidentialité, Intégrité, Disponibilité).

Maintenant, notre objectif est de prévenir les menaces de sécurité telles que les attaques par injection SQL, la question posée (Comment empêcher les attaques par injection SQL à l'aide de PHP), soit plus réaliste, le filtrage des données ou l'effacement des données d'entrée est le cas lors de l'utilisation de données saisies par l'utilisateur dans un tel la requête, l'utilisation de PHP ou de tout autre langage de programmation n'est pas le cas, ou comme recommandé par davantage de personnes d'utiliser une technologie moderne telle qu'une instruction préparée ou tout autre outil prenant actuellement en charge la prévention des injections SQL, considérez que ces outils ne sont plus disponibles ?Comment sécurisez-vous votre candidature ?

Mon approche contre l'injection SQL est la suivante :effacer les données saisies par l'utilisateur avant de les envoyer à la base de données (avant de les utiliser dans une requête).

Filtrage des données pour (Conversion de données non sécurisées en données sécurisées)Considérez que AOP et MySQLi indisponible, comment sécuriser votre candidature ?Est-ce que vous m'obligez à les utiliser ?Qu’en est-il des autres langages autres que PHP ?Je préfère fournir des idées générales car elles peuvent être utilisées pour une frontière plus large et pas seulement pour une langue spécifique.

  1. Utilisateur SQL (privilège utilisateur limité) :les opérations SQL les plus courantes sont (SELECT, UPDATE, INSERT), alors pourquoi accorder le privilège UPDATE à un utilisateur qui n'en a pas besoin ?Par exemple connexion et pages de recherche n'utilisez que SELECT, alors pourquoi utiliser des utilisateurs de base de données dans ces pages avec des privilèges élevés ?RÈGLE:ne créez pas un utilisateur de base de données pour tous les privilèges, pour toutes les opérations SQL, vous pouvez créer votre schéma comme (deluser, selectuser, updateuser) comme noms d'utilisateur pour une utilisation facile.

voir Principe du moindre privilège

  1. Filtrage des données :avant de créer une requête, les entrées utilisateur doivent être validées et filtrées. Pour les programmeurs, il est important de définir certaines propriétés pour chaque variable d'entrée utilisateur :type de données, modèle de données et longueur des données.un champ qui est un nombre compris entre (x et y) doit être exactement validé en utilisant une règle exacte, pour un champ qui est une chaîne (texte) :le modèle est le cas, par exemple, le nom d'utilisateur ne doit contenir que quelques caractères, disons [a-zA-Z0-9_-.] la longueur varie entre (x et n) où x et n (entiers, x <=n ).Règle:créer des filtres exacts et des règles de validation est la meilleure pratique pour moi.

  2. Utilisez d'autres outils :Ici, je serai également d'accord avec vous sur le fait que les instructions préparées (requête paramétrée) et les procédures stockées, les inconvénients ici sont que ces méthodes nécessitent des compétences avancées qui n'existent pas pour la plupart des utilisateurs, l'idée de base ici est de faire la distinction entre la requête SQL et les données. qui est utilisé à l'intérieur, les deux approches peuvent être utilisées même avec des données non sécurisées, car les données saisies par l'utilisateur ici n'ajoutent rien à la requête d'origine, telle que (any ou x=x).Pour plus d'informations, veuillez lire Aide-mémoire pour la prévention des injections SQL OWASP.

Maintenant, si vous êtes un utilisateur avancé, commencez à utiliser cette défense comme vous le souhaitez, mais, pour les débutants, s'ils ne peuvent pas implémenter rapidement la procédure stockée et préparer l'instruction, il est préférable de filtrer autant que possible les données d'entrée.

Enfin, considérons que l'utilisateur envoie ce texte ci-dessous au lieu de saisir son nom d'utilisateur :

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Cette entrée peut être vérifiée tôt sans aucune instruction préparée ni procédure stockée, mais par mesure de sécurité, leur utilisation commence après le filtrage et la validation des données utilisateur.

Le dernier point est la détection de comportements inattendus qui nécessitent plus d’efforts et de complexité ;ce n'est pas recommandé pour les applications Web normales.Le comportement inattendu dans l'entrée utilisateur ci-dessus est SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root une fois ces mots détectés, vous pouvez éviter l'entrée.

MISE À JOUR1 :

Un utilisateur a commenté que ce message est inutile, OK !Voici quoi OWASP.ORG fourni:

Défenses primaires :

Option 1:Utilisation d'instructions préparées (requêtes paramétrées)
Option 2:Utilisation de procédures stockées
Option n°3 :Échapper à toutes les entrées fournies par l'utilisateur

Défenses supplémentaires :

Appliquer également :Moindre privilège
Effectuez également :Validation des entrées de la liste blanche

Comme vous le savez peut-être, revendiquer un article doit être étayé par un argument valable, au moins une référence !Sinon, c'est considéré comme une attaque et une mauvaise réclamation !

Mise à jour2 :

Du manuel PHP, PHP :Relevés préparés - Manuel:

Échappement et injection SQL

Les variables liées seront automatiquement échappées par le serveur.Le serveur insère leurs valeurs échappées aux endroits appropriés dans le modèle d'instruction avant l'exécution.Un indice doit être fourni au serveur pour le type de variable liée, pour créer une conversion appropriée.Voir la fonction mysqli_stmt_bind_param () pour plus d'informations.

L'échappement automatique des valeurs au sein du serveur est parfois considéré comme une fonctionnalité de sécurité pour empêcher l'injection SQL.Le même degré de sécurité peut être atteint avec des instructions non préparées si les valeurs d'entrée sont échappées correctement.

Mise à jour3 :

J'ai créé des cas de test pour savoir comment PDO et MySQLi envoient la requête au serveur MySQL lors de l'utilisation d'une instruction préparée :

AOP :

$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

Journal des requêtes :

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi :

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

Journal des requêtes :

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

Il est clair qu'une instruction préparée échappe également aux données, rien d'autre.

Comme également mentionné dans la déclaration ci-dessus The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly, cela prouve donc que la validation des données telle que intval() est une bonne idée pour les valeurs entières avant d'envoyer une requête. De plus, il est préférable d'empêcher les données utilisateur malveillantes avant d'envoyer la requête. approche correcte et valable.

Veuillez consulter cette question pour plus de détails : PDO envoie une requête brute à MySQL tandis que Mysqli envoie une requête préparée, les deux produisent le même résultat

Les références:

  1. Aide-mémoire pour l'injection SQL
  2. Injection SQL
  3. Sécurité des informations
  4. Principes de sécurité
  5. La validation des données

Un moyen simple serait d'utiliser un framework PHP comme CodeIgniter ou Laravel qui ont des fonctionnalités intégrées telles que le filtrage et l'enregistrement actif afin que vous n'ayez pas à vous soucier de ces nuances.

** Avertissement:l'approche décrite dans cette réponse ne s'applique qu'à des scénarios très spécifiques et n'est pas sécurisée puisque les attaques par injection SQL ne reposent pas uniquement sur la capacité d'injecter X=Y.**

Si les attaquants tentent de pirater le formulaire via PHP $_GET variable ou avec la chaîne de requête de l'URL, vous pourrez les récupérer si elles ne sont pas sécurisées.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Parce que 1=1, 2=2, 1=2, 2=1, 1+1=2, etc...sont les questions courantes posées à une base de données SQL d'un attaquant.Peut-être qu'il est également utilisé par de nombreuses applications de piratage.

Mais vous devez faire attention à ne pas réécrire une requête sécurisée à partir de votre site.Le code ci-dessus vous donne une astuce, à réécrire ou à rediriger (cela dépend de toi) cette chaîne de requête dynamique spécifique au piratage dans une page qui stockera les informations de l'attaquant adresse IP, ou MÊME LEURS COOKIES, leur historique, leur navigateur ou toute autre information sensible, afin que vous puissiez les traiter plus tard en bannissant leur compte ou en contactant les autorités.

Il y a tellement de réponses pour PHP et MySQL, mais voici le code pour PHP et Oracle pour empêcher l'injection SQL ainsi que l'utilisation régulière des pilotes oci8 :

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

Une bonne idée est d'utiliser un 'mappeur relationnel-objet' comme Idiome:

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Cela vous évite non seulement les injections SQL, mais aussi les erreurs de syntaxe !Prend également en charge des collections de modèles avec chaînage de méthodes pour filtrer ou appliquer des actions à plusieurs résultats à la fois et à plusieurs connexions.

Avertissement obsolète :L'exemple de code de cette réponse (comme l'exemple de code de la question) utilise PHP MySQL extension, qui était obsolète dans PHP 5.5.0 et entièrement supprimée dans PHP 7.0.0.

Avertissement de sécurité:Cette réponse n'est pas conforme aux meilleures pratiques de sécurité. L'échappement est insuffisant pour empêcher l'injection SQL, utiliser déclarations préparées plutôt.Utilisez la stratégie décrite ci-dessous à vos propres risques.(Aussi, mysql_real_escape_string() a été supprimé dans PHP 7.)

En utilisant AOP et MYSQLi est une bonne pratique pour empêcher les injections SQL, mais si vous voulez vraiment travailler avec les fonctions et requêtes MySQL, il serait préférable d'utiliser

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Il existe d'autres capacités pour empêcher cela :comme identifier - si l'entrée est une chaîne, un nombre, un caractère ou un tableau, il existe de nombreuses fonctions intégrées pour le détecter.De plus, il serait préférable d'utiliser ces fonctions pour vérifier les données d'entrée.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

Et c'est tellement mieux d'utiliser ces fonctions pour vérifier les données d'entrée avec mysql_real_escape_string.

J'ai écrit cette petite fonction il y a plusieurs années :

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Cela permet d'exécuter des instructions dans un String.Format C#-ish à une seule ligne comme :

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Cela échappe compte tenu du type de variable.Si vous essayez de paramétrer les noms de tables et de colonnes, cela échouera car il mettra chaque chaîne entre guillemets, ce qui est une syntaxe invalide.

MISE À JOUR DE SÉCURITÉ :La précédente str_replace La version autorisait les injections en ajoutant {#} jetons dans les données utilisateur.Ce preg_replace_callback la version ne pose pas de problèmes si le remplacement contient ces jetons.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top