OVH Cloud OVH Cloud

Controle des donnees entrees

28 réponses
Avatar
Pascale
Bon, j'essaye une autre question...

Je travaille sur un site où des utilisateurs peuvent entrer des textes via
des formulaires.
Toutes les pages sont en iso-8859-15. Une fois contrôlées et validées, ces
données sont entrées dans diverses tables MySQL en UTF-8 Unicode.
Évidemment, faudrait pas qu'un utilisateur, par volonté de nuire ou par
ignorance, puisse entrer des données dangereuses...
Je suis allée voir la FAQ et j'ai recherché dans les archives, ce qui m'a
amenée à cette discussion : http://tinyurl.com/y4qycc et à la page
http://www.miakinen.net/vrac/charsets/

J'ai donc essayé d'appliquer les conseils donnés :
$ok = preg_match("|^([- @!%&()/*+?;:.0-9A-Za-z]|\xE2\x82\xAC|\xC2[\xA0-
\xBF]|\xC3[\x80-\xBF])*$|",$string);

Mais je me fais jeter à partir de xE2 :

Warning: preg_match(): Unknown modifier 'â'

En fait, tous les caractères codés en hexa sont rejetés.
Est-ce normal ?

Comment faut-il faire le test pour autoriser presque tout (sauf ce qui
pourrait être dangereux) :
- les caractères alphanumériques, y compris accentués, minuscules et
majuscules, cédilles en minuscule et majuscule
- les signes de ponctuation, apostrophes comprises, et si possible les
guillemets doubles
- quelques caractères spéciaux : &,@,/, symboles monétaires, parenthèses,
crochets, vrais guillements «», espaces insécables, si possible les ½, æ...

Bref, faut-il/peut-on les lister directement les caractères et écrire par
exemple (j'abrège) :

$masque="^([- @!%&()/*+?;:._&'"0-9A-Za-zéèêëçÇàâäùÉÈÊË])*$";

Ou y a-t-il un moyen plus adapté, plus pratique ?

--
Pascale

8 réponses

1 2 3
Avatar
Olivier Miakinen

- Cela suffit-il à empêcher les utilisateurs d'entrer des scripts vérolés
(je cite l'aide : « htmlspecialchars() est pratique pour éviter que des
données fournies par les utilisateurs contiennent des balises HTML, comme
pour un forum ou un chat. ») ? [...]


En fait, dans une valeur d'attribut, les balises HTML on s'en tape un
peu. Les caractères dangereux ne sont pas les mêmes à l'intérieur d'une
balise qu'à l'extérieur.

- De manière tout à fait logique, une fois entré dans la table SQL
correspondante, le contenu du champ « C'est mon très "gros" chien » devient
« C'est mon très "gros" chien » : le problème, c'est que si
mon champ fait 30 caractères, « C'est mon très "gros" chien » passe, mais
la version htmlisée, non.


Tu vas t'en vouloir de ne pas y avoir pensé toi-même, j'en suis sûr :
contrôler la longueur *avant* de transformer les caractères en entités !

Toute vérification de la taille des champs côté client est vouée à
'échec de toutes façons. Mettre un SIZE="10" détermine seulement la
taille par défaut du nombre de caractères affichés après
interprétation. Ca n'empêche pas l'utilisateur de saisir
l'Encyclopédia Britannica s'il a du temps à perdre. Enfin on va dire
au moins le premier volume. Ou la lettre A du Petit Larousse Illustré
édition 1929 : les options ne manquent pas.


Oui, mais bon, avec un maxlength="10" l'utilisateur ne peut normalement pas
saisir plus de 10 caractères. Si ?


D'une part je suis pas sûr que ce soit vraiment interdit par le
navigateur, mais aussi et surtout un utilisateur malintentionné
peut parfaitement ignorer cette directive et envoyer n'importe
quelle requête.


Avatar
John GALLET
- Cela suffit-il à empêcher les utilisateurs d'entrer des scripts vérolés
(je cite l'aide : « htmlspecialchars() est pratique pour éviter que des
données fournies par les utilisateurs contiennent des balises HTML, comme
pour un forum ou un chat. ») ? [...]


En fait, dans une valeur d'attribut, les balises HTML on s'en tape un
peu. Les caractères dangereux ne sont pas les mêmes à l'intérieur d'une
balise qu'à l'extérieur.


Ne pas trop tirer de plans sur cette comète là, en termes de caractères
comme en termes de "cheval de troie" dans un attribut perdu. Il y a des
gens qui savent être super inventifs dans ce registre.

Un petit tour sur http://ha.ckers.org/xss.html permet de se faire des
frayeurs. Personnellement, j'adore ce genre là :

<IMG SRC='vbscript:msgbox("XSS")'>
ou
<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">

qui cassent totalement toute tentative de recherce de mot clef.

- De manière tout à fait logique, une fois entré dans la table SQL
correspondante, le contenu du champ « C'est mon très "gros" chien » devient
« C&#039;est mon très &quot;gros&quot; chien » : le problème, c'est que si
mon champ fait 30 caractères, « C'est mon très "gros" chien » passe, mais
la version htmlisée, non.


Tu vas t'en vouloir de ne pas y avoir pensé toi-même, j'en suis sûr :
contrôler la longueur *avant* de transformer les caractères en entités !


Le problème est surtout d'estimer à l'avance quelle taille on veut en
sgbd. Mettons qu'on estime que le luser ne rentrera pas plus de 50 chars.
Oui, mais 50 chars passés à htmlentities, ça fait combien ? 100 ? 200 ? On
se retrouve vite à mettre des BLOB à la place d'un char(20) :-)

JG


Avatar
Pascale
John GALLET écrivait
news::

Le problème est surtout d'estimer à l'avance quelle taille on veut en
sgbd.


Oui, et comme tu le disais dans un autre message, il y a aussi un impact
sur les tri et les recherches. Je comptais mettre un moteur de recherche
sur les noms de plantes entrés dans la table, voire sur le descriptif
des plantes en question... Et je travaille aussi sur un autre site où les
tris alphabétiques (noms d'associations) sont beaucoup plus employés, et
les users encore plus... euh... inventifs.

 Mettons qu'on estime que le luser ne rentrera pas plus de 50
chars. Oui, mais 50 chars passés à htmlentities, ça fait combien ? 100
? 200 ? On se retrouve vite à mettre des BLOB à la place d'un char(20)
:-)


Maishhheuuuuuu.... (o;
--
Pascale

Avatar
Pascale
John GALLET écrivait
news::

Ca dépend où on l'appelle et ça dépend de quel type d'attaque. Petite
galerie des horreurs possibles et quelques remèdes sur
http://www.saphirtech.com/securite.html


Grmpf. Je suis sûre que c'est affreux, je m'absente jusqu'à mardi, je crois
que je vais garder ça pour mon retour.

En effet, si on fait la modification avant le stockage, comme je
l'indiquais déjà dans ce thread, il faut prévoir des tailles
supérieures. Cela a aussi d'aures effets de bord, en particulier sur
le ORDER BY.


[OUI] et ça m'embête un peu.

problème, mais j'en connais qui risquent quand même de couiner parce
que le
Rôôh, meuuuh non, les utilisateurs ? Couiner pour ça ? Pourquoi

*seulement* pour ça ?


Ce serait étonnant en effet (o; Il y a forcément ceux qui vont trouver que
« c'était mieux avant », ceux qui vont trouver que c'est écrit trop petit,
trop gros, que les couleurs sont trop ternes, ou au contraire font mal aux
yeux, qu'il n'y a pas de CSS et que c'est une honte en 2007 (le site ne
sera pas en ligne avant), que l'aide n'est pas assez complète, ou au
contraire trop détaillée, etc.

Oui, mais bon, avec un maxlength="10" l'utilisateur ne peut
normalement pas saisir plus de 10 caractères. Si ?
Tout dépend de ce qu'il utilise comme clickodrome. Je t'assure que mon

lynx s'en foutra éperdument, et quand à wget ou curl, je te dis même
pas. Donc il faut voir à quoi/qui sert cette restriction si elle est
faite côté cient. Si c'est pour lui indiquer visuellement qu'au delà
de telle taille il risque de se faire zapper, c'est suffisant.


Oui, c'est le but.
D'une manière générale, je me demande toujours ce que mon code va donner
chez l'utilisateur. Je teste sur Opera, Firefox, IE, je trouverai bien un
pote pour me dire ce qu'ne pense Safari, mais sorti de là...

Bon, reprenons la logique et les contraintes.

On veut saisir un texte dans un clickodrome, le stocker en sgbdr, et
le restituer. Ca devrait pas être si difficile que ça sur le papier.
En pratique, pour pas se faire tarter au passage, c'est moins facile.


Voilà.

Concernant le filtrage des données en entrée, depuis des années,
c'était demerden zie sich elein. Depuis peu, l'extension filter est
disponible : http://fr2.php.net/manual/en/ref.filter.php

A éplucher pour voir si le comportement te convient.


Gloups. Va falloir que je m'y penche sérieusement, ça n'a pas l'air
simple... Le manuel PHP renvoie vers une autre adresse qui a l'air
intéressante elle aussi : http://phpro.org/tutorials/Filtering-Data-with-
PHP.html

Ensuite, faut-il désamorcer en entrée ou en sortie ? Perso je
désamorce en entrée, quitte à refaire l'opération inverse en sortie si
finalement mon canal n'est pas du html. D'aucuns font l'inverse.
Faut-il désamorcer les xss en convertissant les < et les > ou en
utilisant strip_tags ? Bonne question. La seconde bouffe tout ce qui
se trouve entre < et > sans autre forme de procès, même si ce n'est
pas du html : a<b et b>c => ac

Ca m'empêche pas de l'utiliser, tout dépend du besoin potentiel des
utilisateurs d'entrer ce genre de choses. Dans une application de
commerce électronique, le besoin est totalement nul, pas plus en
frontal qu'en backoffice.


Je sais que certains utilisateurs risquent par exemple de saisir dans leurs
commentaires des adresses de sites entre <> pour appuyer leurs propos, mais
dans les autres champs, je peux virer sans état d'âme ce qui se trouverait
entre <>. Je peux aussi préciser dans l'aide de ne pas utiliser de <>, que
tout ce qui sera entre sera ratatiné, mais bon, ça suppose que les
utilisateurs lisent l'aide.

Et enfin les problèmes d'affichage, comme les " perdues dans des
attributs. La seule solution est de les transformer au moment où on
les renvoie vers le navigateur, ou de les avoir transformées avant
stockage dans le sgbd.


Et là, sauf si j'ai pas bien suivi, on retombe sur les problèmes de tri qui
peuvent être perturbés par les caractères spéciaux (reste à voir si ce
serait si catastrophique que ça...) et des champs à prévoir plus grands
dans les tables SQL. Mais, grosso modo, l'& étant peu employée, il n'y
aurait guère que les " " pour être « transformés » si j'utilise par
ailleurs strip-tags. C'est ça, j'ai pas trop dit de c...eries ?

--
Pascale


Avatar
Vincent Lascaux
- De manière tout à fait logique, une fois entré dans la table SQL
correspondante, le contenu du champ « C'est mon très "gros" chien »
devient
« C&#039;est mon très &quot;gros&quot; chien »


La base de donnée devrait contenir l'information (C'est mon très "gros"
chien) plutot que sa représentation sous forme HTML. Ca évitera bien des
soucis quand on voudra utiliser l'information pour autre chose que du HTML
(et du même coup ca résoud tes autres problemes). Donc stoquer les données
telles quelles dans la base et utiliser htmlentities sur les données qu'on
affiche sur la page (qu'elles viennent de l'utilisateur ou pas).

--
Vincent

Avatar
Pascale
Vincent Lascaux écrivait
news:45845ace$0$9757$:

La base de donnée devrait contenir l'information (C'est mon très
"gros" chien) plutot que sa représentation sous forme HTML. Ca évitera
bien des soucis quand on voudra utiliser l'information pour autre
chose que du HTML (et du même coup ca résoud tes autres problemes).


Oui !

Donc stoquer les données telles quelles dans la base et utiliser
htmlentities sur les données qu'on affiche sur la page (qu'elles
viennent de l'utilisateur ou pas).


J'avoue que je ne comprends pas trop : si je fais cela, est-ce que mes
données seront protégées contre l'injection de scripts malveillants et
autres méchancetés ?

--
Pascale

Avatar
John GALLET
La base de donnée devrait contenir l'information (C'est mon très
"gros" chien) plutot que sa représentation sous forme HTML.
Vieux débat.



Ca évitera bien des soucis
Ca évitera un appel de fonction qu'on ferait sinon systématiquement dans


le cas de l'output html...

quand on voudra utiliser l'information pour
autre chose que du HTML
Encore faut(il en avoir le besoin.



(et du même coup ca résoud tes autres problemes).
Non.



Donc stoquer les données telles quelles dans la base et utiliser
htmlentities sur les données qu'on affiche sur la page (qu'elles
viennent de l'utilisateur ou pas).


J'avoue que je ne comprends pas trop : si je fais cela, est-ce que mes
données seront protégées contre l'injection de scripts malveillants et
autres méchancetés ?


Ca dépend lesquelles et ça dépend des traitements.

Deux méthodes avec leurs caractéristiques.
Tronc commun aux deux méthodes, but éviter les injections SQL.

Les données brutes sont dans $_GET/POST/REQUEST. On échappe les ' (et "
selon la syntaxe SQL) et on vérifie que les entiers sont bien des entiers.
Ceci permet de faire sereinement des requêtes SQL, par exemple pour
stocker du texte qu'on renverra plus tard à un navigateur.

Maintenant le besoin est de protéger le navigateur en sortie contre les
XSS.

Méthode 1: on n'a pas envie d'oublier un htmlentities/sp dans un coin en
sortie. Donc on désamorce avant stockage au même endroit que ce qui fait
le traitement précédent. Ca a des effets de bord, comme les tailles, order
by etc. Si on veut un autre canal de sortie, ou on prend le méthode 2 ou
on fera l'opération inverse sur ce canal. Quand on fait un echo quel qu'il
soit, venant de la base ou d'une variable locale, c'est bien echo, on a
désamorcé avant. Moi je ne supporte pas l'idée de devoir faire attention à
chaque ligne de code parce que j'ai des variables piégées: un jour ou
l'autre, malgré toute l'attention que je porte à mon codage, j'en
oublierai une, c'est évident "by design".

Méthode 2: on se trimballe des variables piégées partout. Donc on les
stocke en base de données, ce qui ne créee pas d'effets de bord à ce
niveau là. Quand on veut les renvoyer au navigateur, on dératise. Dit
autrement, on doit bannir toute utilisation de echo/print/printf(%s) et
avoir une fontion/méthode qui démine systématiquement la sortie en
fonction du canal.

On ne peut pas bestialement travailler avec ob_start() et htmliser tout le
flux de retour car y compris le html serait rendu inopérant, ou alors il
faut jouer avec une callback et des marqueurs et du parsing. Si on
utilise un moteur de templates un peu intelligent, on doit pouvoir faire
avec, en ne s'emmêlant pas les crayons quand on génère dynamiquement un
lien a href par exemple.

Les deux méthodes ont leurs avantages/inconvénients. Je suis venu à la
première par factorisation du code et simplification. La seconde est plus
logique, plus "normale". Mais trop besogneuse à mon goût, si faille il y a
on ne la verra que quand il sera trop tard. L'essentiel est de faire un
choix conscient des forces et faiblesses de ses méthodes.

Attention à ce propos : dans les choses qu'on renvoie au navigateur, il y
a trois catégories.

- le texte pur. Là on interdit les < et > pour désamorcer, ça suffit.
- le texte acceptant le html. Là les ennuis sérieux commencent.
- les données binaires. N'oubliez pas que les images et les vidéos et
flash SONT DES VECTEURS DE XSS (et d'injections de code php) et doivent
être désamorcés COMME LES AUTRES. Et là je serai catégorique, on désamorce
AVANT stockage, *AUCUNE* raison n'existe de le faire à l'envoi.

a++
JG


Avatar
Pascale
John GALLET écrivait
news::

Deux méthodes avec leurs caractéristiques.
Tronc commun aux deux méthodes, but éviter les injections SQL.

Les données brutes sont dans $_GET/POST/REQUEST. On échappe les ' (et
" selon la syntaxe SQL) et on vérifie que les entiers sont bien des
entiers. Ceci permet de faire sereinement des requêtes SQL, par
exemple pour stocker du texte qu'on renverra plus tard à un
navigateur.


D'acc.

Maintenant le besoin est de protéger le navigateur en sortie contre
les XSS.

Méthode 1: on n'a pas envie d'oublier un htmlentities/sp dans un coin
en sortie. Donc on désamorce avant stockage au même endroit que ce qui
fait le traitement précédent. [couic]


À lire ça, je préfère la première méthode, sans hésitation.

Les deux méthodes ont leurs avantages/inconvénients. Je suis venu à la
première par factorisation du code et simplification. La seconde est
plus logique, plus "normale". Mais trop besogneuse à mon goût, si
faille il y a on ne la verra que quand il sera trop tard. L'essentiel
est de faire un choix conscient des forces et faiblesses de ses
méthodes.


Je ne sais pas si mes raisons sont les bonnes, mais en effet, la première
méthode me semble plus sûre, plus logique.

Attention à ce propos : dans les choses qu'on renvoie au navigateur,
il y a trois catégories.

- le texte pur. Là on interdit les < et > pour désamorcer, ça suffit.
- le texte acceptant le html. Là les ennuis sérieux commencent.
- les données binaires. N'oubliez pas que les images et les vidéos et
flash SONT DES VECTEURS DE XSS (et d'injections de code php) et
doivent être désamorcés COMME LES AUTRES. Et là je serai catégorique,
on désamorce AVANT stockage, *AUCUNE* raison n'existe de le faire à
l'envoi.


Dans le site sur lequel je travaille actuellement, les utilisateurs peuvent
passer du texte brut et des images.

Si je comprends bien, pour le texte brut, on peut à la rigueur se borner à
échapper ' et " et à interdire < et >. Est-ce que ça vaut le coup de faire
plus compliqué ?
Pour les images, je teste plusieurs choses : le poids avec
$_FILES['photo2']['size'] < 20 car j'ai vu que si la photo transmise est
d'une taille supérieure à celle qui est précisée dans MAX_FILE_SIZE, je
peux me retrouver avec un fichier fantôme de quelques octets. Bien sûr, je
teste aussi l'extension, en allant voir la chaîne de caractères qui est
derrière le . , afin de savoir si c'est un jpeg, jpg, JPG ou JPEG. Je
vérifie aussi que $_FILES['photo2']['tmp_name'] n'est pas vide et que
$_FILES['photo2']['error'] est bien égal à 0.
Pour l'instant j'en suis là, c'est-à-dire pas bien loin (: .

--
Pascale

1 2 3