securite, sql, formulaire

16 réponses
Avatar
ricoh51
Bonjour,
C'est mon premier message sur ce groupe, je débute en php.
Sur le site du zéro, j'ai trouvé un tuto qui explique ce qu'il faut
faire pour prévenir les injections SQL et les failles XSS :
function bdd($string){ // Données entrantes
if(ctype_digit($string)){
// On regarde si le type de string est un nombre entier (int)
$string = intval($string);
}
else{ // Pour tous les autres types
$string = mysql_real_escape_string($string);
$string = addcslashes($string, '%_');
}
return $string;
}

Si je comprends bien, je dois toujours utiliser la fonction bdd si
j'exécute une requête sql à partir d'une donnée d'un formulaire.
Mais je ne comprends pas bien l'intérêt de regarder si la donnée est un
nombre...
Et je ne comprends pas non plus l'intérêt de addcslashes, je croyais que
c'était le boulot de mysql_escape_string.

function html($string){ // Données sortantes
return htmlentities($string, ENT_COMPAT, 'UTF-8');
}
Et là je dois utiliser la fonction html lorsque je veux écrire dans la
page des données qui proviennent d'un formulaire.

Pouvez-vous me dire si ces deux fonctions sont correctes?
Et y'at-il des cas qui ne sont pas gérés par ces deux fonctions?

Merci d'avance

eric

10 réponses

1 2
Avatar
Mickael Wolff
Le 02/06/2010 22:32, ricoh51 a écrit :

Sur le site du zéro, j'ai trouvé un tuto qui explique ce qu'il faut
faire pour prévenir les injections SQL et les failles XSS :



Ou plutôt ce qu'il ne faut pas faire. c'est assez usuel sur ce site.

function bdd($string){ // Données entrantes
if(ctype_digit($string)){
// On regarde si le type de string est un nombre entier (int)
$string = intval($string);
}



Aucun intérêt. Ce n'est pas à la couche d'abstraction de la base de
données de s'occuper de déterminer quelles sont les données valides ou
les convertir.

else{ // Pour tous les autres types
$string = mysql_real_escape_string($string);



La seule chose logique.

$string = addcslashes($string, '%_');



Comme le dit la doc de MySQL, mysql_real_escape_string n'échappe pas
% et _ car il n'y a pas de raison de les échappés dans le cas général.


Si je comprends bien, je dois toujours utiliser la fonction bdd si
j'exécute une requête sql à partir d'une donnée d'un formulaire.



Non. La fonction est trop bourrin, pas assez fine. Elle fait fis du
contexte. C'est justement en conseillant ce genre de choses qu'on
encourage les trous de sécurité subtils.

Mais je ne comprends pas bien l'intérêt de regarder si la donnée est un
nombre...



Aucun, surtout qu'on risque un bogue si l'entier est supérieur à 2^16 - 1

function html($string){ // Données sortantes
return htmlentities($string, ENT_COMPAT, 'UTF-8');
}
Et là je dois utiliser la fonction html lorsque je veux écrire dans la
page des données qui proviennent d'un formulaire.



Surtout pas. Encore une fois, l'échapement des caractères doit être
fait en fonction du contexte (textnode, attrnode, etc) et non pas
échapés brutalement sans aucun discernement. Sans compte qu'ici, on ne
gère pas du tout les attaques se basant sur un encodage différent.


--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Michael DENIS
Le 02/06/2010 23:32, ricoh51 a écrit :
C'est mon premier message sur ce groupe, je débute en php.
Sur le site du zéro, j'ai trouvé un tuto qui explique ce qu'il faut
faire pour prévenir les injections SQL et les failles XSS :



Je ne connais pas ces différentes fonctions, mais je serai tenté de vous
donner un conseil simple : pour éviter beaucoup de problèmes, *il suffit
déjà de vérifier que l'on reçoit ce que l'on a demandé*.

Mais encore ? Si j'ai un champ "liste déroulante" par exemple et que la
valeur reçue n'est pas dans la liste ou si je mets un champ avec une
longueur maximum et que j'en reçois plus, il y a vraisemblablement un
problème...

--
Michaël DENIS
Avatar
ricoh51
Le 03/06/2010 08:39, Mickael Wolff a écrit :
Le 02/06/2010 22:32, ricoh51 a écrit :

else{ // Pour tous les autres types
$string = mysql_real_escape_string($string);



La seule chose logique.



ok, alors prenons un exemple simple : j'ai un espace membre, et je veux
vérifier que le pseudo n'existe pas encore au moment de l'inscription.
J'accepte tous les caractères dans un pseudo, la seule restriction est
que sa longueur doit être comprise entre 6 et 20 caractères. Je fais :

if(isset(pseudo)){ //Vérif du pseudo
$pseudo2 = mysql_real_escape_string($pseudo);
$sql="SELECT COUNT(*) AS verif_exist FROM visiteur WHERE
pseudoVisiteur='$pseudo2'";
$reponse=mysql_query($sql);
$donnees = mysql_fetch_array($reponse);
if ($donnees['verif_exist'] != 0){ // le pseudo existe déja
$erreurPseudo='Ce pseudo existe déjà.';
}
$longueur=mb_strlen($pseudo, 'UTF-8');
if(($longueur<6)||($longueur>20)){
$erreurPseudo='La longueur du pseudo doit être comprise entre 6 et
20 caractères.';
}
}

Et lorsque le compte est créée, et que le gars se connecte, je vérifie
le pseudo puis j'affiche le pseudo dans la page :

$pseudo=$_POST['pseudo'];
$pass = $_POST['password'];
$pseudo2 = mysql_real_escape_string($pseudo);
$pass2 = mysql_real_escape_string($pass);
// [snip] la verif qu'il y a $pseudo2 dans la base
$sql = "select * from visiteur WHERE pseudoVisiteur='".$pseudo2."'";
$reponse=mysql_query($sql);
$donnees = mysql_fetch_array($reponse);
if(sha1($pass2)==$donnees['passVisiteur']){ // ici c'est bon
$_SESSION['pseudo']=$pseudo;
}

// et plus tard
echo '<p>Bonjour ' . htmlentities($_SESSION['pseudo']) . '</p>';

function html($string){ // Données sortantes
return htmlentities($string, ENT_COMPAT, 'UTF-8');
}
Et là je dois utiliser la fonction html lorsque je veux écrire dans la
page des données qui proviennent d'un formulaire.



Surtout pas. Encore une fois, l'échapement des caractères doit être fait
en fonction du contexte (textnode, attrnode, etc)



je ne comprends pas textnode, attrnode...


et non pas échapés
brutalement sans aucun discernement. Sans compte qu'ici, on ne gère pas
du tout les attaques se basant sur un encodage différent.



Ah? Peux tu développer?

Merci beaucoup

eric
Avatar
John GALLET
Bonjour,

C'est mon premier message sur ce groupe, je débute en php.


"Bienvenue chez les fous" (tm).

Sur le site du zéro, j'ai trouvé un tuto qui explique ce qu'il faut
faire pour prévenir les injections SQL et les failles XSS :



Il y a plusieurs types d'injections sql, et encore plus de types
d'injections xss, ne serait-ce que savoir si elles sont "instantanées"
ou "stockées".

Si je comprends bien, je dois toujours utiliser la fonction bdd si
j'exécute une requête sql à partir d'une donnée d'un formulaire.



Il faut toujours filtrer ses données en entrée, c'est à dire vérifier si
ce qu'on reçoit est bien conforme à ce qu'on attend, y compris comme
déjà suggéré selon des listes de valeurs possibles, mais quand on reçoit
un identifiant muet type autoincrement, ce n'est pas faisable.

Et je ne comprends pas non plus l'intérêt de addcslashes, je croyais que
c'était le boulot de mysql_escape_string.



Bof. Ca fait partie des querelles de clocher.

Et là je dois utiliser la fonction html lorsque je veux écrire dans la
page des données qui proviennent d'un formulaire.



C'est pas aussi simple que ça, là non plus. Si elle vient à l'origine du
monde extérieur et qu'elle a été stockée en base de données, elle ne
vient pas "directement" du formulaire, mais elle peut néanmoins être
dangereuse si on n'a pas fait le boulot avant.
Là encore, querelles de clocher. Personnellement je ne stocke pas des
bombes amorcées dans ma cave: je désamorce d'abord, puis je stocke.
D'autres font le filtrage en sortie de la base de données.

Pouvez-vous me dire si ces deux fonctions sont correctes?


Clairement: non. Même pas le début de commencement des prémisces.

Et y'at-il des cas qui ne sont pas gérés par ces deux fonctions?


Un début de réponse sur un document PDF que j'ai rédigé il y quelques
années déjà, synthèse d'un thread assez long sur fr.comp.securite

http://www.saphirtech.com/securite.html

HTH
JG
Avatar
ricoh51
Le 03/06/2010 08:39, Michael DENIS a écrit :
Le 02/06/2010 23:32, ricoh51 a écrit :
C'est mon premier message sur ce groupe, je débute en php.
Sur le site du zéro, j'ai trouvé un tuto qui explique ce qu'il faut
faire pour prévenir les injections SQL et les failles XSS :



Je ne connais pas ces différentes fonctions, mais je serai tenté de vous
donner un conseil simple : pour éviter beaucoup de problèmes, *il suffit
déjà de vérifier que l'on reçoit ce que l'on a demandé*.

Mais encore ? Si j'ai un champ "liste déroulante" par exemple et que la
valeur reçue n'est pas dans la liste ou si je mets un champ avec une
longueur maximum et que j'en reçois plus, il y a vraisemblablement un
problème...



oui c'est ce que je crois comprendre : Être parano et avoir beaucoup de
bon sens :)

eric
Avatar
ricoh51
Le 05/06/2010 15:12, John GALLET a écrit :
Un début de réponse sur un document PDF que j'ai rédigé il y quelques
années déjà, synthèse d'un thread assez long sur fr.comp.securite

http://www.saphirtech.com/securite.html



merci pour ce lien, je "comprends" maintenant que la sécurité c'est
compliqué :) C'est plutôt flippant je trouve...

Existe-t-il un document du même genre qui donne l'état de l'art (en
2010) pour réaliser un site web avec un espace membre? (pseudo, mot de
passe, mail de confirmation...)

eric
Avatar
Jean-Francois Ortolo
Le 07/06/2010 23:37, ricoh51 a écrit :
Le 05/06/2010 15:12, John GALLET a écrit :
Un début de réponse sur un document PDF que j'ai rédigé il y quelques
années déjà, synthèse d'un thread assez long sur fr.comp.securite

http://www.saphirtech.com/securite.html



merci pour ce lien, je "comprends" maintenant que la sécurité c'est
compliqué :) C'est plutôt flippant je trouve...

Existe-t-il un document du même genre qui donne l'état de l'art (en
2010) pour réaliser un site web avec un espace membre? (pseudo, mot de
passe, mail de confirmation...)

eric




Bonjour Monsieur

Pour ce qui est des données reçues par un formulaire, on peut
utiliser les fonctions php ad hoc.

Par exemple, pour le cas de la variable object reçue en mode post par
le script :

$object = filter_input( INPUT_POST, 'object', FILTER_SANITIZE_STRING,
FILTER_FLAG_ENCODE_HIGH | FILTER_FLAG_ENCODE_LOW );

if( $object === NULL OR $object === false OR empty( $object )
// traitement d'erreur
else
{
// Traitement OK.
}

Pas besoin de s'embarrasser de filtrages perso par des expressions
rationnelles plus ou moins foireuses, php fait le boulot.

Il y a un certain nombre de ces filtres associés aux fonctions
filter_imput() et autres... C'est dans le PHP Manual.

Bien à vous.

Amicalement.

Jean-François Ortolo
Avatar
Pascal
ricoh51 a écrit :
merci pour ce lien, je "comprends" maintenant que la sécurité c'est
compliqué :) C'est plutôt flippant je trouve...



Bonjour,

Faudrait pas devenir parano, non plus! ;-)
Il me semble que le secret d'une sécurisation bien faite consiste
d'abord à l'adapter aux enjeux.

On peut faire le parallèle avec les portes blindées et leurs serrures
multipoint.
Si je n'ai à protéger que des livres de poches et des croûtes au mur, je
ne prendrais ni le même prestataire, ni le même contrat d'assurance, que
si je possède une collection de livres rares et des toiles de maîtres.

Existe-t-il un document du même genre qui donne l'état de l'art (en
2010) pour réaliser un site web avec un espace membre? (pseudo, mot de
passe, mail de confirmation...)



Donc oui, bien sûr, il existe d'autres recommandations, mais pas
forcément adaptées à tout projet ou réalisation.

Dans le "haut de gamme", on peut trouver cela :
http://www.owasp.org/index.php/Category:OWASP_PHP_Project

Chaque framework (cadriciel) proposera son modèle de sécurité, comme par
exemple celui de Zend :
http://framework.zend.com/manual/en/learning.multiuser.html

D'autres permettent d'intégrer des plugins, comme Symfony :
http://www.symfony-project.org/plugins/filter

Tout ça répond à la préoccupation d'avoir quelque chose de sérieux sans
devoir tout réinventer.
Sinon, comme Jean-François le faisait justement remarquer, il existe une
bibliothèque au moins dédiée au filtrage, dans PHP :
http://fr2.php.net/manual/en/book.filter.php

Cordialement,
Pascal
Avatar
Mickael Wolff
Le 05/06/2010 14:12, John GALLET a écrit :
Et je ne comprends pas non plus l'intérêt de addcslashes, je croyais
que c'était le boulot de mysql_escape_string.



Bof. Ca fait partie des querelles de clocher.



Ce n'est absolument pas une querelle de clocher. addcslashes est une
fausse bonne solution. Lorsqu'on envoie des données à travers une
interface, il faut toujours s'assurer d'utiliser la fonction qui filtre
le mieux la donnée. Et addcslashes, la plupart du temps, n'ayant pas une
connaissace approfondie du problème, ne permet pas de filtrer
correctement les données. Utiliser la fonction qui est faite pour (par
exemple mysql_real_escape_string dans le cas de l'usage du module
mysql), est la seule solution valable.

C'est pas aussi simple que ça, là non plus. Si elle vient à l'origine du
monde extérieur et qu'elle a été stockée en base de données, elle ne
vient pas "directement" du formulaire, mais elle peut néanmoins être
dangereuse si on n'a pas fait le boulot avant.
Là encore, querelles de clocher. Personnellement je ne stocke pas des
bombes amorcées dans ma cave: je désamorce d'abord, puis je stocke.
D'autres font le filtrage en sortie de la base de données.



Et c'est comme ça qu'on se retrouve avec des entités HTML au beau
milieu de documents PDF ou d'images sans même s'en rendre compte. Je
considère la base de données comme une source extérieure de données (une
base de données est faite pour être partagée, c'est une vue persistante
des données métiers).


Et y'at-il des cas qui ne sont pas gérés par ces deux fonctions?


Un début de réponse sur un document PDF que j'ai rédigé il y quelques
années déjà, synthèse d'un thread assez long sur fr.comp.securite

http://www.saphirtech.com/securite.html



Beaucoup de choses avec lesquelles je ne suis pas d'accord, mais on
ne va pas troller :p

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Mickael Wolff
Désolé pour le lag, je suis un peu occupé en ce moment.

Le 03/06/2010 09:59, ricoh51 a écrit :

ok, alors prenons un exemple simple : j'ai un espace membre, et je veux
vérifier que le pseudo n'existe pas encore au moment de l'inscription.
J'accepte tous les caractères dans un pseudo, la seule restriction est
que sa longueur doit être comprise entre 6 et 20 caractères. Je fais :

if(isset(pseudo)){ //Vérif du pseudo
$pseudo2 = mysql_real_escape_string($pseudo);
$sql="SELECT COUNT(*) AS verif_exist FROM visiteur WHERE
pseudoVisiteur='$pseudo2'";
$reponse=mysql_query($sql);
$donnees = mysql_fetch_array($reponse);
if ($donnees['verif_exist'] != 0){ // le pseudo existe déja
$erreurPseudo='Ce pseudo existe déjà.';
}
$longueur=mb_strlen($pseudo, 'UTF-8');
if(($longueur<6)||($longueur>20)){
$erreurPseudo='La longueur du pseudo doit être comprise entre 6 et 20
caractères.';
}
}



Ici tu mélange allègrement la logique métier (un pseudo a telle
taille) et la logique de contrôle (aiguillage de la requête). C'est
malheureusement la méthode canonique sous PHP. Je ne vais pas trop m'en
éloigner pour ne pas te perdre, et te proposes plutôt ceci :

<?php

$errors = array('pseudo' => array()) ;

if(isset($pseudo)) // Cette condition n'est pas suffisante
{
$sql = sprintf('SELECT COUNT(*) AS verif_exist FROM visiteur WHERE
pseudoVisiteur = '%s''
, mysql_real_escape_string($pseudo));

$result = mysql_query($sql) ;
$row = mysql_fetch_array($result) ;
if ($row['verif_exist'] > 0)
$errors['pseudo'][] = 'Ce pseudo existe déjà.' ;

mysql_free_result($result) ;

$longueur = mb_strlen($pseudo, 'UTF-8') ; // Es-tu certain d'avoir de
l'UTF-8 en entrée ?
if($longueur < 6 || $longueur > 20)
$errors['pseudo'][] = 'La longueur du pseudo doit être comprise entre
6 et 20 caractères.' ;
}
else
$errors['pseudo'][] = 'Ooops' ;


echo '<p>Bonjour ' . htmlentities($_SESSION['pseudo']) . '</p>';



htmlspecialchars suffit amplement.

je ne comprends pas textnode, attrnode...



<http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html>
TextNode
<http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1312295772>
AttrNode
<http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-637646024>

Ah? Peux tu développer?



<http://shiflett.org/blog/2005/dec/googles-xss-vulnerability>

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
1 2