Lorsque vous écrivez du SQL - pour tout ce qui nécessite vraiment un apport humain, beaucoup de choses ont été faites pour éviter l'injection.
Tous ceux qui ont entendu parler de l'injection SQL savent que (je vais utiliser PHP comme exemple) faire quelque chose comme ça n'est pas sûr:
$sql = "SELECT * FROM `users`
. WHERE `userName` = "{$_POST["username"]}"
. AND `pass` = "{$_POST["pass"]}";";
Puis, bien sûr, quelqu'un a eu l'idée d'utiliser des "citations d'échappement magique" pour gérer les entrées de programme qui n'étaient pas correctement nettoyées et directement mises en sql à la suite de mauvaises pratiques. Cela n'a pas vraiment résolu le problème de l'injection SQL, mais cela signifiait que toutes les entrées utilisateur étaient tronquées.
Ainsi, certaines personnes ont désactivé les citations magiques. Ensuite, ils ont analysé les entrées utilisateur avant le point de SQL via addslashes()
qui en théorie échappe à toutes les citations et votre pirate ne peut pas faire ' OR 1=1
, Mais même la documentation pour les addlashes c'est soi-même dire que vous ne devriez pas utiliser les ajouts, il dit d'utiliser la fonction spécifique à la base de données telle que mysql_real_escape_string()
, mais certains disent que cela ne suffit pas.
Donc, nous ne pouvons pas utiliser *_real_escape_string
Spécifique au SGBD, nous ne pouvons pas utiliser add slashes
, La chose "guillemets magiques" a causé beaucoup de problèmes, et le Web est plein de citations courtes telles que :
"Un hacker dédié trouvera un moyen de parcourir vos boucles d'échappement, utilisez simplement les instructions préparées par DBAL" - John Q any programmer
D'accord, donc cela m'a fait assez peur pour utiliser des instructions de préparation et un DBAL. Cela n'explique pas vraiment quoi que ce soit, mais ça sonne bien parce que je l'ai beaucoup entendu.
Alors maintenant, nous utilisons PDO, ou un DBAL à partir d'un framework, ou quelque chose d'autre qui enveloppe tout notre sql et s'assure que quelqu'un ne peut pas exécuter une injection sql.
Ma question est essentiellement un "pourquoi pas?", Pas un "que dois-je utiliser?". Le Web est plein de gens qui vous disent d'utiliser ceci ou d'utiliser cela ou quoi que ce soit , mais aucune explication de la raison pour laquelle ces choses devaient se produire.
Questions pointues (rappel, je pose des questions sur SQL, PHP était un exemple de langage à cause de sa mauvaise réputation autour de SQL, les concepts sont universels):
Pourquoi ne pouvons-nous pas échapper à toutes les entrées des utilisateurs en utilisant la "magie"?
Au moment où la magie est appliquée, on ne sait pas où les données finiront. Les citations magiques détruisent donc les données qui, à moins qu'elles ne soient écrites sans échapper dans une base de données.
Il peut simplement être utilisé dans la réponse HTML renvoyée au client. Pensez à un formulaire qui n'a pas été complètement rempli et qui est donc à nouveau affiché à l'utilisateur. Avec les guillemets magiques, les données entrées lors de la première tentative seront désormais échappées SQL, ce qui n'a aucun sens sur une page HTML. Pire encore: lors de la deuxième soumission, les données sont à nouveau échappées SQL.
Pourquoi les addlashes n'étaient-ils pas "assez bons"?
Il a des problèmes avec les caractères multi-octets:
' 27
\ 5c
뼧 bf 5c
Ce sont deux octets, mais un seul caractère Unicode.
Puisque addslashes
ne sait rien sur Unicode, il convertit l'entrée bf 27
à bf 5c 27
. S'il est lu par un programme compatible Unicode, il est considéré comme 뼧 '. Boom.
Il y a une bonne explication de ce problème à http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string
Quel est le problème avec l'utilisation des fonctions d'échappement spécifiques à la base de données, et pourquoi était-ce mieux que les ajouts de flash?
Ils sont meilleurs car ils garantissent que l'échappant interprète les données de la même manière que la base de données (voir la dernière question).
D'un point de vue de la sécurité, ils sont corrects si vous les utilisez pour chaque entrée de base de données unique. Mais ils ont le risque de les oublier quelque part.
Edit : Comme l'a ajouté getahobby: Ou que vous utilisez xxx_escape_strings pour les nombres sans ajouter de guillemets autour d'eux dans l'instruction SQL et sans vous assurer qu'il s'agit bien de nombres réels par transtypage ou conversion de l'entrée dans le type de données approprié /Modifier
Du point de vue du développement logiciel, ils sont mauvais car ils rendent beaucoup plus difficile l'ajout d'un support pour d'autres logiciels de serveur de base de données SQL.
Pourquoi les instructions préparées avec des cadres et PDO sont-elles saluées comme l'étalon-or de SQL? Pourquoi sont-ils meilleurs?
PDO est surtout une bonne chose pour des raisons de conception de logiciels. Cela facilite grandement la prise en charge d'autres logiciels de serveur de base de données. Il possède une interface orientée objet qui résume bon nombre des petites incompatibilités spécifiques à la base de données.
Pourquoi ne puis-je pas faire une injection SQL avec ces [déclarations préparées], alors que je pourrais avoir avec les moyens mentionnés précédemment?
La partie " requête constante avec paramètres variables " des instructions préparées est ce qui est important ici. Le pilote de base de données échappera automatiquement à tous les paramètres sans que le développeur ait à y penser.
Les requêtes paramétrées sont souvent plus faciles à lire que les requêtes normales avec des paramètres d'échappement pour des raisons syntaxiques. Selon l'environnement, ils peuvent être un peu plus rapides.
Toujours utiliser des instructions préparées avec des paramètres est quelque chose qui peut être validé par des outils d'analyse de code statique . Un appel manquant à xxx_escape_string n'est pas repéré aussi facilement et de manière fiable.
Un programmeur ne parvient-il pas d'une manière ou d'une autre à gâcher cela? À quoi doivent-ils faire attention?
Les "instructions préparées" impliquent que les constantes are . La génération dynamique d'instructions préparées - en particulier avec une entrée utilisateur - présente toujours tous les problèmes d'injection.
[1.] Pourquoi ne pouvons-nous pas échapper à toutes les entrées des utilisateurs en utilisant la "magie"?
Parce que les citations magiques sont brisées pour deux raisons:
addslashes
(voir ma note sur [2.]), donc ils ne sont pas réellement sécurisés.[2.] Pourquoi les addlashes n'étaient-ils pas "assez bons"?
Il existe de nombreuses façons de sortir de la restriction addslashes
, par conséquent, il est fondamentalement défectueux, mais vous essayez de l'utiliser.
[3.] Qu'est-ce qui ne va pas avec l'utilisation des fonctions d'échappement spécifiques à la base de données, et pourquoi était-ce mieux que les ajouts?
Rien vraiment. C'est simplement le mantra sur la raison pour laquelle les AOP/préparés sont "meilleurs". Ils sont façon mieux que addslashes
car ils prennent en charge de nombreux facteurs, y compris l'encodage. Voici un petit secret; PDO les utilise en interne, mais pas addslashes
...
[4.] Pourquoi les déclarations préparées avec des frameworks et PDO sont-elles saluées comme l'étalon-or de SQL? Pourquoi sont-ils meilleurs? Pourquoi ne puis-je pas faire une injection SQL avec ceux-ci, alors que je pourrais avoir avec les moyens mentionnés précédemment?
Ils sont pas la norme d'or, ils sont pas "plus" sécurisés que les fonctions spécifiques à la base de données et vous pouvez faire l'injection SQL avec ces ainsi que. Et non, vous ne pouvez pas faire d'injection SQL avec une entrée correctement filtrée.
Comme pour toutes les technologies, les développeurs ont tendance à développer des tendances religieuses (fanatiques?) Pour quelque chose de "nouveau". C'est pourquoi vous obtenez des ingénieurs certifiés Zend chevronnés (TM) vous conseillant de ne pas vous forcer à passer aux déclarations préparées.
La réalité, cependant, est que tout dépend de savoir ce que vous faites. Si vous chargez un article par son ID numérique, vous n'avez pas besoin de s'échapper, encore moins de PDO. Vous devez simplement vous assurer que l'ID est bien un entier.
D'un point de vue plus réaliste, la règle générale n'est pas d'utiliser des instructions AOP/préparées, mais d'utiliser les bonnes fonctions, c'est-à-dire que vous devez utilisez mysql_real_escape_string()
au lieu de addslashes()
ou mysql_escape_string()
.
[5.] Un programmeur ne parvient-il pas, d'une manière ou d'une autre, à tout gâcher? À quoi doivent-ils faire attention?
Bien sûr! Mais ce n'est pas "ce qu'il faut rechercher" mais plutôt "savoir ce que vous faites".
Vous voyez, eval($_GET['code'])
* est tout à fait légitime lorsqu'il est utilisé au bon endroit (tel qu'un local PHP Test Runner).
Si vous utilisez un ORM pour exécuter une requête conditionnelle, vous entrez directement du code, il peut donc y avoir une chance d'obtenir une injection SQL si vous ne le faites pas correctement (cela dépend de l'ORM en question). (Hypothétique) Exemple:
// update() sets the value of a column given a condition
//
// .--escaping is automatic here
// | .--hypothetical SQL injection
// v v
Db()->update('name',$_GET['newname'])->where(' id = '.$_GET['id']);
(* Je ne peux qu'imaginer les innombrables professionnels se cogner la tête dessus)
À la fin de la journée, l'échappement des entrées doit être fait afin de protéger vos requêtes via SQL Injection. Les bibliothèques de requêtes paramétrées comme PDO et ADODB utilisent des échappements , elles ne font qu'appliquer cette pratique d'une manière implicitement sécurisée. Par contraste, magic_quotes_gpc ne [~ # ~] pas [~ # ~] , c'est une approche fondamentalement erronée du problème.
1) L'évasion peut être problématique si vous ne comprenez pas ce que vous faites. Ce type de problème est toujours exploitable si magic_quotes_gpc est activé.
mysql_query("select * from users where id=".addslashes($_GET[id]));
Exploit PoC: http: //localhost/vuln.php? Id = sleep ( )
patch:
mysql_query("select * from users where id='".addslashes($_GET[id])."'");
Leçon: Vous n'avez pas besoin de guillemets pour exploiter l'injection sql.
2) Disons que vous n'échappez qu'aux guillemets:
mysql_query("select * from users where name='".htmlspecialchars($_GET[name],ENT_QUOTES)."' and id='".htmlspecialchars($_GET[id],ENT_QUOTES)."'");
Exploit PoC: http: //localhost/vuln.php? Name = \& id = + ou + sleep (30)/*
Patch:
mysql_query("select * from users where name='".addslashes($_GET[name])."' and id='".addslashes($_GET[id])."'");
Leçon : les barres obliques inverses sont tout aussi dangereuses que les guillemets.
3) Qu'en est-il du codage linguistique?
mysql_query("SET CHARACTER SET 'gbk'");
mysql_query("select * from user where name='".addslashes($_GET[name])."'");
Exploit PoC: http: //localhost/vuln.php? Name =% bf% 27 = sleep (30)/*
Patch:
mysql_query("SET CHARACTER SET 'gbk'");
mysql_query("select * from user where name='".mysql_real_escape_string($_GET[name])."'");
Conclusion:
Un échappement est absolument nécessaire pour éviter l'injection sql dans une application. Dans PHP PDO et ADOB sont d'excellentes bibliothèques de requêtes paramétrées qui imposent l'échappement des entrées utilisateur. Le problème est que de nombreux programmeurs ne comprennent pas ce qu'est l'échappement. Avoir une bibliothèque pour appliquer cette politique de sécurité est la meilleure méthode de défense, car vous n'avez pas besoin de comprendre les règles qui s'échappent.
À propos des fonctions d'échappement spécifiques à DB. Il existe de nombreux scénarios dans lesquels une injection SQL peut se produire, même si mysql_real_escape_string () a été utilisé. LIMIT et ORDER BY sont vraiment délicats!
Ces exemples sont vulnérables à l'injection SQL:
$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
$sql = "SELECT userid, username FROM sql_injection_test LIMIT $offset, 10";
ou
$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
$sql = "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) <- Lisez ceci
Les filtres peuvent être contournés et les données qui entrent dans les instructions SQL peuvent provenir de plus d'endroits que la simple entrée utilisateur. Les points d'entrée dans une source/récepteur influencé par SQL peuvent être stockés (ordre supérieur), à titre d'exemple, mais clairement les paramètres HTTP GET et POST des formulaires HTML ne sont pas les seuls moyens de l'utilisateur contribution.
Les requêtes paramétrées ne libèrent pas nécessairement vos instructions SQL des injections. Ils nécessitent également une liaison variable, la suppression des opérations/concaténation de chaînes et l'évitement de certains problèmes de sécurité avec un large éventail de clauses SQL.
La plupart des développeurs oublient également de vérifier leurs composants tiers et autres dépendances pour les problèmes d'injection SQL, et ne réalisent parfois pas que ces inclusions pourraient rendre leur propre code vulnérable.
La meilleure chose à faire est d'embaucher une société de conseil en sécurité des applications pour préparer vos applications à la préparation à la production et de former votre ou vos équipes appdev à l'intégration des contrôles de sécurité des applications dans leur technologie de processus et de développement.
Pour développer la réponse d'Hendrick. Il est facile de penser à real_escape_string comme une balle magique alors qu'en réalité ce n'est pas le cas. J'ai vu des gens utiliser cette fonction lorsqu'ils souhaitent utiliser intval ou convertir l'entrée en un entier à la place. Vous pouvez toujours être injecté si vous utilisez la chaîne d'échappement dans le mauvais contexte.