Ganf
jeudi 3 février 2005 à 16:50
Pour prendre les choses depuis le début (parce que des fois avoir le contexte peut aider à comprendre) :
1. Quand on fait une requête SQL, les délimiteurs de chaînes de caractères sont des apostrophes. Pour utiliser une chaîne qui contient une apostrophe dans une requête SQL il faut l'échapper. L'échapper c'est à dire la mettre sous une forme reconnue par le serveur de base de données. C'est généralement soit en la précédant d'un backslash (\') soit en la doublant (''). Cet échappement est "mangé" par le serveur de base de données, il n'est donc pas nécessaire de l'enlever quand on récupère la donnée qu'on y avait inséré.
Si on l'oublie toutes les chaînes qui contiennent des apostrophes feront des requêtes SQL qui posent problème (soit qui échouent, soit qui font un problème de sécurité)
Sous PHP la fonction qui permet de rajouter des backslash est addslashes(). Il existe une fonction qui fait l'opération inverse nommée stripslashes().
2. Au début de PHP les développeurs de PHP ont pensé qu'il serait une bonne idée de faciliter la vie de l'utilisateur en faisant automatiquement ces addslashes, comme ça il ne risque pas d'oublier.
Il y a donc une directive de configuration dans le php.ini qui s'appelle "magic_quotes_gpc". Quand elle est activé (ce qui était le cas par défaut et chez quasiment tous les hébergeurs) PHP fait un addslashes sur tout ce qu'il récupère venant de l'utilisateur (formulaires, cookies, valeurs dans l'URL).
Ce que tu vois c'est simplement cette directive en action.
- Tu rentres : l'exemple. PHP réceptionne et ajoute les slash, ton script reçoit : l\'exemple.
- Ton script affiche ce qu'il a reçu : l\'exemple. Tu valides donc tu renvoies cette chaîne. PHP la reçoit a nouveau, il va rajouter un slash devant chaque slash et chaque apostrophe. Ton script va donc recevoir l\\\'exemple et te l'afficher.
3. Le problème arrive donc comme c'est que les deux postulats fait au (2) est faux.
- Toutes les données ne sont pas destinées à passer en SGBD. La plupart sont même faites pour être réaffichées directement pour confirmation validation, ou traitées par code avant insertion. Au lieu d'oublier de faire des addslashes on oublie de faire des stripslashes là où le addslashes fait par défaut était inutile/mauvais.
- Toutes les données ne viennent pas de l'utilisateur. C'est le plus gros problème de ce fonctionnement. On va devoir savoir si une chaîne vient de formulaire, de fichier, de base ou d'ailleurs pour savoir comment la traiter (savoir si elle a déjà eu un addslashes ou pas). Au lieu de simplifier on complexifie grandement.
Du coup maintenant la politique c'est de désactiver le magic_quotes_gpc dans la configuration. C'est ce qui est fait dans la config par défaut des dernières versions de PHP.
Le problème qui survient est que du coup on ne sait pas toujours dans quelle configuration on est. Il y a donc la fonction get_magic_quotes_gpc() qui retourne true ou false suivant qu'elle est activée ou pas.
Je vous propose donc 2x3 solutions au choix (par ordre de préférence) :
** En prenant pour base le fait de ne pas faire un addslashes automatique (conseillé)
A- Tu changes la configuration de ton serveur pour mettre magic_quotes à Off (c'est la valeur conseillée et par défaut sur toutes les versions récentes de PHP) et tu n'oublies pas de faire un addslashes() ou un mysql_real_escape_string() à chaque fois que tu met une chaîne de caractère dans une requête SQL.
B- Tu n'as pas la main sur la configuration ou tu veux un script portable. Tu fais donc comme au A mais tu exécutes le code suivant à chaque début de page pour "corriger" PHP si jamais la directive de configuration était activée :
CODE
function recursiveStripSlashes($a) {
if (is_array($a)) {
foreach($a as $name => $value) {
$a[$name] = recursiveStripSlashes($value);
}
} else {
$a = stripslashes($a);
}
return $a;
}
if (get_magic_quotes_gpc()) {
$_REQUEST = recursiveStripSlashes($_REQUEST);
$_GET = $HTTP_GET_VARS = recursiveStripSlashes($_GET);
$_COOKIE = $HTTP_COOKIE_VARS = recursiveStripSlashes($_COOKIE);
$_POST = $HTTP_POST_VARS = recursiveStripSlashes($_POST);
}
C- Tu fais comme A mais à chaque fois que tu utilises une donnée de REQUEST/POST/GET/COOKIE tu la fais passer à la main dans la fonction correctStripSlashes() suivante :
CODE
function correctStripSlashes($a) {
if (get_magic_quotes_gpc()) $a = recursiveStripSlashes($a);
return $a;
}
function recursiveStripSlashes($a) {
if (is_array($a)) {
foreach($a as $name => $value) {
$a[$name] = recursiveStripSlashes($value);
}
} else {
$a = stripslashes($a);
}
return $a;
}
*** Les mêmes dans le sens inverse (déconseillé) :
A- Tu changes la configuration de ton serveur pour mettre magic_quotes à On (c'est la valeur conseillée et par défaut sur toutes les versions récentes de PHP) et tu n'oublies pas de faire un stripslashes() à chaque fois que tu met une chaîne de caractère qui vient de l'utilisateur ailleurs que dans une requête SQL.
B- Tu n'as pas la main sur la configuration ou tu veux un script portable. Tu fais donc comme au A mais tu exécutes le code suivant à chaque début de page pour "corriger" PHP si jamais la directive de configuration était activée :
CODE
function recursiveAddSlashes($a) {
if (is_array($a)) {
foreach($a as $name => $value) {
$a[$name] = recursiveAddSlashes($value);
}
} else {
$a = addslashes($a);
}
return $a;
}
if (!get_magic_quotes_gpc()) {
$_REQUEST = recursiveAddSlashes($_REQUEST);
$_GET = $HTTP_GET_VARS = recursiveAddSlashes($_GET);
$_COOKIE = $HTTP_COOKIE_VARS = recursiveAddSlashes($_COOKIE);
$_POST = $HTTP_POST_VARS = recursiveAddSlashes($_POST);
}
C- Tu fais comme A mais à chaque fois que tu utilises une donnée de REQUEST/POST/GET/COOKIE tu la fais passer à la main dans la fonction correctAddSlashes() suivante :
CODE
function correctAddSlashes($a) {
if (!get_magic_quotes_gpc()) $a = recursiveAddSlashes($a);
return $a;
}
function recursiveAddSlashes($a) {
if (is_array($a)) {
foreach($a as $name => $value) {
$a[$name] = recursiveStripSlashes($value);
}
} else {
$a = stripslashes($a);
}
return $a;
}