OVH Cloud OVH Cloud

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

6 réponses

1 2
Avatar
ricoh51
Le 10/06/2010 12:51, Mickael Wolff a écrit :
Désolé pour le lag, je suis un peu occupé en ce moment.



ben c'est déjà très sympa de me répondre, je ne vais pas me plaindre en
plus :)


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).



oui... pas bien, j'ai déjà eu des périodes d'intégrisme d'abstraction
(pas en php je débute), mais pour des petits projets comme ça, je me
demande si ça vaut bien la peine?

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()) ;



je ne comprends pas cette construction... je ne domine pas assez les
"tableaux" de php je crois :)


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));



ok, ici pour un pseudo, la vérification du type (le %s) n'apporte rien
je pense, mais j'imagine que c'est une bonne habitude à prendre.

mysql_free_result($result) ;



pareil, j'imagine que c'est une bonne habitude à prendre de libérer la
mémoire.


$longueur = mb_strlen($pseudo, 'UTF-8') ; // Es-tu certain d'avoir de
l'UTF-8 en entrée ?



Ma page comporte :
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
et ma base de données est entièrement en utf-8. Je voulais être
tranquille pour le jour où mon_beau_site_de_la_mort sera traduit en
chinois, coréen etc :)


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.



ok

Ah? Peux tu développer?



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



pfff, il faut devenir un vrai pirate pour faire un site sans trop de
trou de sécurité :(

Je te remercie beaucoup

eric
Avatar
John GALLET
Re,

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



Non, c'est juste le syndrôme JCVD: "il faut être aware"... Ne jamais
faire quelque chose sans en comprendre les implications, avantages et
inconvénients.


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...)



Il est "dans l'état de l'art" au sens que nulle nouvelle forme d'attaque
spécifique n'est apparue depuis, juste des raffinements. Ce qui manque,
c'est une bonne couche sur la gestion correcte des charsets.

En revanche, sur l'implémentation pour y arriver, ce document n'étant
même pas "spécifique" à php, il y a plein de moyens.

a++;
JGA
Avatar
John GALLET
Re,

oui... pas bien, j'ai déjà eu des périodes d'intégrisme d'abstraction
(pas en php je débute), mais pour des petits projets comme ça, je me
demande si ça vaut bien la peine?



Honnêtement: oui. Il faut au moins séparer:
- la décision préalable "j'ai bien reçu une requête cohérente et valide"
- la couche d'interaction avec le stockage
- la couche de "présentation"/sortie, qu'elle soit en html, pdf, excel...


je ne comprends pas cette construction... je ne domine pas assez les
"tableaux" de php je crois :)



Si ça peut t'aider: ce sont des hash tables. Une clef référence une
donnée (qui peut elle même être un autre tableau).

$sql = sprintf('SELECT COUNT(*) AS verif_exist FROM visiteur WHERE
pseudoVisiteur = '%s''
, mysql_real_escape_string($pseudo));



ok, ici pour un pseudo, la vérification du type (le %s) n'apporte rien
je pense, mais j'imagine que c'est une bonne habitude à prendre.



Là encore, plusieurs méthodes possibles. Même avec un %d pour "forcer"
un entier, moi je préfère vérifier que c'en est un avant de décider que
je peux faire la requête, et si je m'attends à recevoir un entier
obligatoire comme donnée, si je ne reçois pas un entier, je ne fais même
pas de tentative de requête sql. Cas dans lequel le sprintf %d ne me
sert strictement plus à rien vu que j'ai déjà validé que **c'est** est
un entier.

Accessoirement, algorithmiquement, ça ne sert à rien de faire un select
pour vérifier qu'un pseudo n'existe pas: c'est le boulot de la base de
données. Tu fais ton insert, et si la clef primaire/unique te renvoie
DUPLICATE_KEY... bah c'est qu'il existe déjà... Voir "la stratégie
d'insert/update dans la FAQ de ce forum, http://faqfclphp.free.fr/


mysql_free_result($result) ;



pareil, j'imagine que c'est une bonne habitude à prendre de libérer la
mémoire.


Oui, car en théorie PHP le fait tout seul, mais il y a déjà eut des bugs
où l'appel automatique ne se faisait pas.

$longueur = mb_strlen($pseudo, 'UTF-8') ; // Es-tu certain d'avoir de
l'UTF-8 en entrée ?



Ma page comporte :
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
et ma base de données est entièrement en utf-8.



Deux choses:

- vérifie ce que le serveur web envoie comme HEADER à l'insu de ton
plein gré, car il prime sur ce que tu mets dans ton http-equiv.

- ce n'est pas parce que tu demandes au client (navigateur) de t'envoyer
de l'utf-8 que moi, gentil cracker de base, je ne vais pas justement
t'envoyer du coréen avec wget.

Relis mon document, c'est important: NE JAMAIS partir du principe qu'on
reçoit ce qu'on a demandé. On peut parfaitement "mettre hors jeu" en
rejetant violemment tout ce qui est de manière évidente de la bidouille,
mais il faut le faire, pas partir du principe que ça n'arrivera pas.

Ce dont tu es sûr, sauf si le serveur http envoie avant ton script un
header disant que le charset est autre chose, c'est que la navigateur
est censé comprendre ton output comme utf-8. Tu n'es pas sûr que tu vas
en recevoir. Tu "espères" en recevoir, et si c'est autre chose, tu peux
envoyer sur les roses.


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



htmlspecialchars suffit amplement.





Rien du tout suffit amplement vu que tu dis envoyer un charset utf-8 si
on part du principe que de toutes façons tu as auparavant interdit
l'utilisation des XSS dans cette donnée en autorisant exclusivement des
caractères "normaux" dans le pseudo. Il n'y a pas de raison qu'on
admette < ou & dans un pseudo. Même si ici caser une XSS en 20
caractères c'est probablement faisable mais plus contraignant.

pfff, il faut devenir un vrai pirate pour faire un site sans trop de
trou de sécurité :(


Il faut en connaître un minimum vital pour ne pas faire une passoire.

a++;
JGA
Avatar
John GALLET
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 donc si tu as une webapp qui doit parler à plusieurs SGBDR, il est
donc nul et non avenu de parler de mysql_realmachin ou quoi que ce soit
d'autre. Ce que je veux dire par querelle de clocher c'est que 10
développeurs différents donneront 10 manières différentes de gérer
l'échappement des quotes en SQL selon qu'ils ont l'habitude de
travailler avec mysql, oracle, sybase (qui n'échape pas avec mais avec
une autre '), et je ne parle pas des bidouilles avec access ou autres.


(par exemple mysql_real_escape_string dans le cas de l'usage du module
mysql), est la seule solution valable.


Et si tu as Sybase c'est mort.

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.



Et si on ne le fait pas c'est comme ça comme se retrouve comme un con
avec un PDF qui est en train d'embarquer du JS offensif et est devenu
vecteur d'attaque. Pas plus tard qu'il y a trois jours, l'ISC rappelait
que 28% des attaques de malware sont désormais sur vecteur PDF...

http://isc.sans.edu/diary.html?date 10-06-09
Troisième article.


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).



C'est une possibilité, je dis juste que cette approche nécessite un
niveau élevé de conscience du danger qu'elle
implique et qu'il est **évident** qu'un jour une autre personne qui
travaille avec cette base, aujourd'hui ou après ton départ, se fera
tarter parce que c'est pas "naturellement sécurisé" comme approche et
surtout non intuitif. Une fois de plus: tout l'enjeu est de comprendre
ce que l'on fait, avantages et inconvénients.


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


On peut aussi discuter sans troller... Je n'ai pas la parole divine,
loin s'en faut.

a++;
JGA
Avatar
ricoh51
Un grand merci à tous ceux qui m'ont répondu dans ce fil

eric
Avatar
Mickael Wolff
Le 12/06/2010 16:13, John GALLET a écrit :
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 donc si tu as une webapp qui doit parler à plusieurs SGBDR, il est
donc nul et non avenu de parler de mysql_realmachin ou quoi que ce soit
d'autre. Ce que je veux dire par querelle de clocher c'est que 10
développeurs différents donneront 10 manières différentes de gérer
l'échappement des quotes en SQL selon qu'ils ont l'habitude de
travailler avec mysql, oracle, sybase (qui n'échape pas avec mais avec
une autre '), et je ne parle pas des bidouilles avec access ou autres.



C'est ce que j'ai dit : la fonction qui filtre le mieux, qui est la
plus adaptée pour résoudre le problème. Je rajouterai que la bonne
solution sera unique, et ne dépendra pas de l'habitude du programmeur :
le choix doit dépendre des technologies employées.


(par exemple mysql_real_escape_string dans le cas de l'usage du module
mysql), est la seule solution valable.


Et si tu as Sybase c'est mort.



J'ai dit PAR EXEMPLE. Évidamment que pour Sybase ce sera la fonction
ad hoc qu'il faudra employer. Et c'est justement là où ta méthode
préventive part en sucette.


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.



Et si on ne le fait pas c'est comme ça comme se retrouve comme un con
avec un PDF qui est en train d'embarquer du JS offensif et est devenu
vecteur d'attaque. Pas plus tard qu'il y a trois jours, l'ISC rappelait
que 28% des attaques de malware sont désormais sur vecteur PDF...



Justement non. En utlisant un outil correct de génération de PDF, qui
propose une fonction d'échappement tel qu'il existe avecl'HTML, ça ne
peut pas arriver.

http://isc.sans.edu/diary.html?date 10-06-09
Troisième article.



L'article ne parle pas de PDF générés dynamiquement, mais des PDF
envoyés dans les pourriel. C'est quelque peu hors sujet.


C'est une possibilité, je dis juste que cette approche nécessite un
niveau élevé de conscience du danger qu'elle
implique et qu'il est **évident** qu'un jour une autre personne qui
travaille avec cette base, aujourd'hui ou après ton départ, se fera
tarter parce que c'est pas "naturellement sécurisé" comme approche et
surtout non intuitif. Une fois de plus: tout l'enjeu est de comprendre
ce que l'on fait, avantages et inconvénients.



J'emmerde les incompétents. Et j'emmerde ceux qui les embauche. Je
documente suffisament mon travail pour qu'il soit compris, maintenable
et sécurisé.

Ceci dit, filtrer les données à leur arrivée n'est pas naturel pour
moi. Je peste souvent contre les sites qui restreignent bêtement
l'ensemble des données acceptées par des a priori sans réalité. Ces
restrictions interviennent souvent parce que c'est une personne qui a
codé le contrôleur, a filtré les fonnées ici, plutôt que de faire appel
à un objet métier. L'imbécilité la plus courante est la restriction au
niveau des courriels. Peu de développeurs savent que les adresses
suivantes sont syntaxiquement valides :

mickael+
"mickael toto"@example.com

Séquestrer le test sur les adresses e-mail dans une classe et
documenter une classe métier est presque un gage de pérénité du test.

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


On peut aussi discuter sans troller... Je n'ai pas la parole divine,
loin s'en faut.



Une chose qui nous différencie :p

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