Des actualités personnelles sous un style impersonnel, et inversement.
Follow @Thomas_Jannaud
First part in French, 2nd part in English
L'une des choses les plus embêtantes à gérer dans un site est l'échappement de guillemets pour éviter les injections MySQL. Et même sur un site qui tourne depuis 3 ans il y a toujours à régler des petits bugs.
Les problèmes peuvent venir de plusieurs endroits :
INSERT INTO comments('$_POST["message"])
, on va avoir des problèmes si l'utilisateur rentre qqchose comme "1); DROP DATABASE xxx;". On s'en protège en mettant des \ devant les guillemets et les apostrophes.$_POST['message'] = "Je m'appelle Thomas" ou "Je m\'appelle Thomas"
, en fonction du serveur.Quand on voit ça, on panique un peu et on va tout échapper une fois, deux fois, trois fois. Et après on voit que quand on affiche ça en html on obtient des "Je m\\\\'appelle Thomas", alors on va faire stripslashes 2 ou 3 fois avant d'afficher les commentaires en html. D'une part ça ne résoudra pas le problème et d'autre part des entrées dans la base de données auront été insérées avec l'ancien code pour échapper les chaînes, d'autres avec votre nouveau code. Bref vous allez vite avoir un truc tout dégoûtant.
La bonne manière de faire est :
mysql_real_escape_string()
ou pdo->quote()
si l'on utilise PDO.htmlspecialchars()
. C'est ce qui fait que les choses comme <script> deviendront <script>En bref :
if (get_magic_quotes_gpc()) {
$_POST['message'] = stripslashes($_POST['message']);
...
}
...
$message = mysql_real_escape_string($_POST['message']);
$query = "INSERT INTO comments(message, ...) VALUES('$message',...)";
...
Si vous utilisez PDO
$message = mypdo->quote($_POST['message']);
et il faudra alors écrire
VALUES($message,...)
au lieu de VALUES('$message',...)
car contrarement à mysql_real_escape_string, quote va ajouter des guillemets devant et derrière le texte au contraire de mysql_real_escape_string.
En voulant corriger certains problèmes et on a décidé de refaire les entrées dans la BDD pour remplacer tous les \' par des '.
UPDATE comments SET message=REPLACE(message, "\'", "'")
Problème : si on le fait les dates des enregistrements concernés seront changé à aujourd'hui si elles ont l'attribut ON UPDATE CURRENT_TIMESTAMP.
Un moyen de contourner le problème :UPDATE comments SET date=date, message=REPLACE(message, "\'", "'")
Dernière chose pour conclure : n'oubliez pas de toujours utiliser utf-8 partout. Ça vous permettra d'écrire é au lieu de &ecute; ainsi que des choses comme 馬鹿. Utilisez utf8_bin plutôt que les autres options proposées, l'ordre lexicographique sera l'ordre naturel des entiers/octets et ça sera beaucoup plus pratique si vous interfacez MySQL avec d'autres langages.
One of the annoying things about writing a website in PHP/MySQL is to escape every input string to avoid sql injections. This website has been up and running for more than 3 years and yet another little bug has just been fixed. Again.
Problems can arise in various places :
INSERT INTO comments('$_POST["message"])
, we will have problems if the user starts to enter things like "1); DROP DATABASE xxx;". We can protect against these by escaping quotes : \'$_POST['message'] = "I'm Joe" or "I\'m Joe"
, depending on the server.When you see that, you are super afraid not to escape quotes enough that you panic, escape strings twice, and soon you will have some "I\\\'m Joe" on your html. So you will strip them twice when printing the comments in html. And so on. And some entries in your database will be inserted with some first version escaping code and some others with the second version. And you will have some bugs and things will get messy and dirty.
The right way to do it is :
mysql_real_escape_string()
or pdo->quote()
if using PDO.htmlspecialchars()
when printing them on html (so that things like <,script> become <script>)In brief :
if (get_magic_quotes_gpc()) {
$_POST['message'] = stripslashes($_POST['message']);
...
}
...
$message = mysql_real_escape_string($_POST['message']);
$query = "INSERT INTO comments(message, ...) VALUES('$message',...)";
...
Note that if you use a PDO object, do
$message = mypdo->quote($_POST['message']);
and in the query line do VALUES($message,...)
instead of VALUES('$message',...)
because there are already some quotes before and after the text, contrary to mysql_real_escape_string.
After fixing the slash problems in the code, we fixed them in the database, by doing something like :
UPDATE comments SET message=REPLACE(message, "\'", "'")
The problem is that by doing so, all the timestamps of the changed records will be set to today's date if the date in your table structure have their default attribute set to ON UPDATE CURRENT_TIMESTAMP. You won't be able to revert this if you do not have backed up your database.
One way to avoid that is to do : UPDATE comments SET date=date, message=REPLACE(message, "\'", "'")
Among other tips, don't forget to set all your encodings to utf-8. This will enable non English people to write letters like é, ç or 馬鹿. Use utf8_bin among all other options as it will define the lexicographic order to be the same as of the integers/bytes which is very useful when interfacing MySQL with other programming languages.
Laissez un commentaire !
Pas besoin de vous connecter, commencez à taper votre nom et une case "invité" apparaîtra.