Comment proteger des injections SQL ?

nub (non connecté)
Invité n'ayant pas de compte PHPfrance

11 juil. 2008, 17:02

bonjour,

Je suis en train de faire un script de minitchat. Tout marche sauf la protection de mon texte. Quand je rajoute "->quote(), plus rien ne s'enregistre dans ma base de donnée pourtant on m'a dit que pour protéger ses données li fallait faire comme ça. Quand je met rien par contre sa marche mais les données ne sont pas protéger!


J'aimerais donc savoir comment protéger mes données sans activer la fonction get_magic_quote() !

Merci d'avance.

div id="minitchat">
	<div class="corps_menu">
	
	<?php 
	$login = "root";
	$pass = "";
	$dsn = 'mysql:host=localhost;dbname=test';
	try {
	
	$dbh = new PDO($dsn, $login, $pass);
	} catch (Exception $e) {
	print "erreur ! :" . $e.getMessage() . "<br />";
	die();
	}
	
	if (!empty($_POST['pseudo']) and !empty($_POST['message']))
			{
		
					$pseudo = ($_POST['pseudo']); // variable a protéger 
					$message = ($_POST['message']); // variable a protéger 
				
					
					$sql = "INSERT INTO minichat VALUES('', '".$pseudo."', '".$message."', '".time()."')";
					$resultat = $dbh->exec($sql);

			}
	?>
	<?php
		$reponse = "SELECT * FROM minichat ORDER BY id DESC LIMIT 0,15";
		$sth = $dbh->query($reponse);
		
		while ($donnees = $sth->fetch() )
		{
	?>
		
		<p class="minchat"><strong><span class="form_pseudo"><?php echo $donnees['pseudo']; ?></span></strong><br / >
		<?php echo date('d/m/Y à H\hi', $donnees['timestamp']); ?><br />
		<?php echo $donnees['message']; ?><hr />
		
		</p>
		
	<?php
	}
	$dbh = NULL; 	
	?>
	
	<form action="index.php" method="post">
	<p>
	<input type="text" name="pseudo" value="<?php echo $_POST['pseudo']; ?>" /><br />
	<input type="text" name="message" /><br />
	
	<input type="submit" value="envoyer" />
	</p>
	</form>
	
	
	</div>
</div>
</body>
</html>

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

11 juil. 2008, 18:43

Alors tout d'abord, on ne se protège pas des "infections" mais des "injections" sql. MySQL n'est pas un gros virus mutant qui vient nous empoisonner la vie ;) En revanche, on prend soin de ne pas laisser un utilisateur glisser un code incidieux dans nos requêtes SQL :)

En très résumé, le principe de l'injection c'est quand tu vérifies qu'un utilisateur s'authentifie avec la requête :
"SELECT * FROM table WHERE login = '$login' "
et qu'un petit malin s'amuse à spécifier la valeur " ' OR login != ' " à la place de son login. La requête générée devient alors :

Code : Tout sélectionner

"SELECT * FROM table WHERE login = '' OR login != '' "
ce qui a pour effet de retourner tous les enregistrements dont le login est vide ou pas.

Pour s'en protéger, c'est assez simple, il suffit de protéger les caractères spéciaux pouvant avoir un impact sur la requête générée. C'est le role de la fonction mysql_real_escape_string() qui va nottament échapper les apostrophes avec un antislash. Pour reprendre l'exemple précédent, le code :
"SELECT * FROM table WHERE login = '".mysql_real_escape_string($login)."' "
va générer la requête

Code : Tout sélectionner

"SELECT * FROM table WHERE login = '\' OR login != \'' "
Ce qui aura pour effet de ne trouver aucun résultat (ou alors tes utilisateurs ont des logins bizares ;))


Nota : si les magic quotes sont activées (ce que tu peux savoir avec la fonction get_magic_quote()) php va également ajouter des "\" devant les apostrophes lorsqu'il récupère des données en GET ou en POST. Il faut donc gérer le cas ou cette directive est activée pour supprimer l'antislash ajouté par les magick quotes (avec stripslashes() par exemple) pour éviter que le real escape string ne les échappe à nouveau : \\\' OR login != \\\'
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

nub
Invité n'ayant pas de compte PHPfrance

11 juil. 2008, 20:56

Oups dsl pour la faute :D.

J'ai déjà essayer avec mysql_real_escape_string ... Cependant Je l'utilisais avec l'ancienne méthode(mysql_connect()). Seulement maintenant, j'utilise la PDO et donc quand je marque mysql_real_escape_string une erreur s'affiche parce que ce n'est pas ce code qu'il faut utiliser.

j'ai essayé en faisant :

$pseudo = $dbh->quote($_POST['pseudo'];
$message = $dbh->quote($_POST['message'];
Quand j'utilise quote() plus rien ne s'enregistre dans ma base de donnée. C'est pour ça que j'aimerais savoir si il y a un autre moyen !

Merci d'avance :)

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

12 juil. 2008, 12:29

Oki, j'avais pas fait attention au fait que tu utilises PDO :)

En principe la méthode quote() devrait effectivement remplacer l'appel au real escape. Toutefois, cette méthode va en plus ajouter des apostrophes autour de la chaine retournée. Il ne faut donc pas que tu en rajoutes en plus dans la requête :
$pseudo = $dbh->quote($_POST['pseudo']); // variable a protéger  
$message = $dbh->quote($_POST['message']); // variable a protéger  
                     
$sql = "INSERT INTO minichat VALUES ('', ".$pseudo.", ".$message.", '".time()."')"; 
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

ViPHP
ViPHP | 5924 Messages

12 juil. 2008, 12:36

Sinon vu que tu utilises pdo, tu peux passer par les requêtes préparées…

nub
Petit nouveau ! | 8 Messages

12 juil. 2008, 13:50

merci beaucoup ça marche de nouveau !

Concernant les requêtes préparées, je ne sais pas trop ce que c'est ..
Si tu fait allusion au magic quotes, je tiens à m'en séparer car elles vont disparaitre avec php 6 !

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

ViPHP
ViPHP | 5924 Messages

12 juil. 2008, 19:21

Euh, non je ne parle pas des magic quotes, sinon j'aurais dit magic quotes, pas requètes préparées :D
http://www.php.net/manual/fr/pdo.prepare.php

Administrateur PHPfrance
Administrateur PHPfrance | 658 Messages

13 juil. 2008, 11:17

Tu peux utiliser l'extension Filter de PHP par exemple.

++
Co-auteur du livre PHP 7 avancé
Co-auteur du livre Performances PHP : Audit et optimisation LAMP
Co-fondateur de l'Association Française des Utilisateurs de PHP http://www.afup.org
Formateur PHP pour Openska

Eléphanteau du PHP | 12 Messages

06 août 2008, 16:04

Puisque je vois qu'il y a déjà une discussion ouverte sur une question que je me pose et qui est en relation avec celle ci, j'en profite.
Avis aux spécialistes de PDO :
Je sais que si j'utilise PDO avec des requêtes préparées je n'ai pas besoin d'utiliser la fonction quote pour les échappements.
Par contre je voudrais savoir si pour les variables à récupérer sur les zones texte de mes formulaires je dois quand même utiliser trim et ce qui s'en suit (voir ci-dessous) ou bien ce n'est plus nécessaire non plus.

Code : Tout sélectionner

$variable=trim(stripslashes(htmlentities($_POST["variable"])));
Merci de vos futures réponses…
:wink:

ViPHP
ViPHP | 5924 Messages

06 août 2008, 16:47

Je vais répeter un principe de base qui est que l'on ne stocke pas les données formattées en base. Les bases de données sont faites pour stocker les données brutes. Pour quelle raison obscure aurait-tu besoin que les entités HTML soient échappées lorsque tu les insères dans la base ? Crois-tu que tu vas avoir une erreur sql parce que tu stockes <html> plutôt que >html< ?

Le formattage des données se fait à l'affichage, ton htmlentities n'a absolument rien à faire ici. Pour trim cela dépend à quoi il sert. Les données stockées en base doivent être les données réelles, c'est à dire après le traitement post-formulaire, et avant le traitement pré-affichage. Il faut donc que tu te demandes si la donnée réelle c'est ' machin machin ' ou 'machin machin', selon ton application, et donc si ton trim doit être un traitement sur la données entrantes ou les données sortantes.

Eléphanteau du PHP | 11 Messages

06 août 2008, 18:43

Pour quelle raison obscure aurait-tu besoin que les entités HTML soient échappées lorsque tu les insères dans la base ?
Peut être pour le faire une fois pour toute plutot que de le refaire à chaque affichage, vive les optims :D

ViPHP
ViPHP | 5924 Messages

06 août 2008, 18:57

Peut être pour le faire une fois pour toute plutot que de le refaire à chaque affichage, vive les optims :D
L'intégrité des données est plus importante que la faible optimisation d'une telle opération. Et si on va par là, une entité html prend plus de place en mémoire que le caractère donc on perd de la performance. Enfin htmlentities est une fonction compilée dans le coeur de php, donc son temps d'exécution est négligeable sur l'échelle del'exécution du script…

Eléphanteau du PHP | 11 Messages

06 août 2008, 23:48

Pour l'impact mémoire vrai je suis d'accord avec toi, mais bon je t'avouerai que je suis pas a 1ko prés sur la génération d'une page, aprés pour le htmlentities compilé dans php je te dirai que preg_match et str_replace le sont aussi et pourtant j'ai deja optmisé des scripts en virant quelques appels à ces fonctions.

Pour le cas de htmlentities je ne connais pas son temps d'éxécution mais je pars du principe qu'il ne sert à rien de faire 1 millions de fois une opération alors qu'on peut la faire une fois pour toute (une page est servie beaucoup plus souvent que modifié), et je suis pret à payer le prix de la non intégrité des données, surtout que les exports de base j'en fait pas des masses.

Aprés c'est une question de choix, j'ai fait le mien, tu as le tien et je le respecte.

Eléphanteau du PHP | 12 Messages

06 août 2008, 23:49

Ah d'accord. En fait je n'avais, il est vrai, pas réfléchi aussi précisément. :oops: C'est par mimétisme que je faisais ça vu que j'apprends sur le tas. J'avais trouvé ça dans des scripts mais ce n'était pas des exemples par PDO, même si ça ne change rien. Donc je recopiais la manière de faire.
Donc c'est inversement, au moment de l'affichage que je vais traiter les infos de cette manière. En fait je faisais ça une fois au moment où les données entraient dans ma base mais je comprends maintenant ce que tu expliques sur l'intégrité des données.
Quand tu parles de traitement post formulaire c'est juste la vérification des champs en fait et voir si par exemple l'adresse mail ressemble bien à une adresse mail? Puis ça part tel quel dans ta base?
J'apprends, j'apprends un peu plus tous les jours.
:ordi:

ViPHP
ViPHP | 5924 Messages

07 août 2008, 00:20

En gros après le formulaire tu modifies les données pour qu'elles soient "valides". A savoir que évidemment tu vérifies la validité des champs (bonne formation d'une adresse mail, d'une ip, d'un pseudo). Tu modifies les données au besoin ou demandes modification (selon la politique du site). Par exemple pour un numéro de téléphone tu peux supprimer les espaces, les points, les tirets, si quelqu'un a placé des séparateurs, pour un pseudo, tu peux éventuellement utiliser un trim, etc…

Dans ta base tu as alors les données les plus naturelles possibles, et toutes sous le même format. Si tu dois appliquer un filtre pour nettoyer le numéro de téléphone (pour notre exemple), tu le fais avant, parce que sinon tu te retrouves avec des données totalement hétérogènes, et tu ne peux à aucun moment t'assurer de l'unicité des données, voire faire une recherche correcte.
Le problème est le même lorsque l'on utilise htmlentities. Comment rechercher des caractères accentués dans une table facilement si on a appliqué htmlentities auparavant. htmlentities est clairement une fonction d'affichage puisqu'elle prépare pour un affichage html. Les données ne sont plus valides pour un autre usage. C'est là l'enjeu. Les données doivent être génériques et valables pour n'importe quel usage. Si tu as appliqué une modification avant, tu brides complètement les usages.
Donc tous les urlencode, htmlentities, traduction de bbcode (comment veux tu modifier un post si le bbcode a déjà été traduit en balises html lors du stockage ?), toutes les fonctions d'affichage en bref doivent être réservées à l'affichage.

Voilà, après à chaque fois cela dépend de ce que tu considères être ta donnée brute, mais voilà des exemples pour t'aider à comprendre…