OVH Cloud OVH Cloud

[Long] Pratiques de codage php et webapps

120 réponses
Avatar
John Gallet
Bonjour,


NB1 : xpost fr.comp.lang.php et fu2 fr.comp.securite (les deux sont
modérés).

Après moult tergiversations, je laisse le fu2 sur fr.comp.securite car
on dépasse le cadre du langage PHP et que les failles à débattre sont
majoritairement
humaines et non liées au langage. J'encourage plus que vivement les
habituels lecteurs/contributeurs de fclphp à suivre la discussion sur
fcs (et à le
consulter régulièrement une fois cette bonne habitude prise ;-)...)

Je me porte volontaire pour essayer de faire une synthèse de la
discussion qu'on pourra intégrer dans l'une des deux FAQs et référencer
depuis l'autre.

NB 2 : Cet article étant long et fcs étant modéré, je rappelle aux
contributeurs qui seraient tentés de le citer intégralement que leur
réponse n'a aucune chance d'être publiée. Si vous avez un doute sur la
bonne manière de répondre sur un forum usenet, usez et abusez de
http://www.giromini.org/usenet-fr/repondre.html


Suite aux divers questions/trolls sur la sécurité des applications
écrites en PHP dans une optique web, je lance un petit débat sur les
pratiques de codage en PHP apportant ou non un vrai "plus" de sécurité.

J'entends par "faille de sécurité" une erreur de codage ou de conception
qui permet de passer outre une procédure d'authentification, d'avoir
accès à des données non publiques, ou de modifier/détruire des
données/des scripts, exclusivement dans une optique web, avec php comme
langage dans mon esrit à l'origine, mais on pourra élargir à d'autres
langages/plateformes de web dynamique comme perl, jsp, asp, .net etc...

Les questions sont les suivantes :

Question 1 :

Quelles sont les principales failles existantes dans les scripts PHP que
vous avez rencontrées ? Quels risques induisaient-elles ? Comment les
avez vous corrigées ?

Question 2 :

Quelles sont les principales fausses vérifications de sécurité que vous
connaissez ? Comment peut-on les contourner (indiquer la difficulté
pour y arriver) ou pourquoi ne sont-elles pas fiables ou non applicables
sur le principe même ?

Question 3 :

Pensez vous à des failles théoriques potentielles que vous n'avez pas
encore vérifiées en pratique ?

------------
Je commence bien entendu :

Question 1 (failles existantes):
a) variables non itilialisées en register_globals=On (injection de
variables)
Risque : principalement accès non autorisés, mais tout est possible.
Correction : initialiser ses variables (Sans blague...), ou utiliser des
fonctions/des objets car ils snt insensibles à l'injection de variables.
Piège : croire qu'on est toujours en register_globals=Off et coder comme
un cochon.
Correction : idem.

b) include dynamiques (ex include($toto);)

Risque : exécution sur sa machine de n'importe quel code souhaité par
l'attaquant (installation de back-doors, défigurations, etc....
Correction : ne pas utiliser d'includes dynamiques ou vérifier que le
fichier est bien local si hébergement dédié. Renforcer les restrictions
d'include_path. Attention, depuis php5, file_exists peut éventuellement
renvoyer TRUE sur des fichiers distants (à restester, je n'ai pas poussé
plus loin que le "tip php5" du manuel).
Piège : essayer de se renforcer avec include($toto.'.php');
Contournement : $toto="[target]/script_sans_extension"; par exemple.

c) injection SQL.
Risque : accès non autorisés, corruption de données
Correction : filtrage des variables, échappement de ' et " par le
caractère ad hoc pour la base de données (\ pour mysql, ' pour sybase
etc...)

d) confiance dans les variables venant de l'extérieur. Par exemple,
recalculer une facture à payer en utilisant un prix transmis par un
champ HIDDEN ou calculé en javascript. Ne pas revalider la donnée parce
qu'elle l'a été en JavaScript.
Risque : multiples. Accès non autorisés, corruption de données, etc...
Correction : ne faire confiance qu'à des données conservées côté serveur
(refaire une requête sgbd pour obtenir le prix de l'article, les frais
de port, etc...). Faire avant tout les validations de cohérence des
données côté serveur et non en javascript.

e) uploads de fichiers.
Outre les failles du langage php lui même qui apparaissent parfois à ce
sujet, les tutoriels que j'ai vus n'insistent pas assez sur le besoin de
faire attention aux extensions autorisées par rapport aux extensions
parsées sur le serveur. Si le serveur considère comme du code php le
fichier toto.php.txt, il faut interdire tout nom de fichier contenant
.php. dans son nom. Ceci doit venir en complément d'une liste
restrictive d'extensions explicitement autorisées (.jpg, .gif, .doc
etc...). Je suis plus particulièrement intéressé sur ce point par les
vérification purement serveur permettant de vérifier le type de fichier
traité.

f) utilisation de header("Location:...)
Algo (erronné)
1. vérification de cohérence
2.1 si problème alors header("Location:bad.php"); // jusqu'ici tout va
bien
2.2 si ok alors header("Location:ok.php"); // et plouf dommage, il
suffit d'appeler directement ok.php avec n'importe quels arguments et
tout passe.
Correction :
2.1 si problème require('erreur.php'); exit();
2.2 (sinon) require('traitement.php'); // rappel : toute variable locale
est alors définie dans traitement.php

g) appels systèmes non filtrés
Dans le même genre que les includes dynamiques, passer directement la
saisie de l'utilisateur à exec() ou system(). Personnellement, j'ai
tendance à interdire tout exécution de code directe, filtrée ou par (je
remplace les actions possibles par des cases à cocher et j'exécute ce
qu'il faut). Peut-être est-ce par trop parano et que 'lon peut autoriser
certaines choses.
Risques : donner la main sur votre machine à un attaquant.
Correction : ne jamais passer quoi que ce soit qui vient de l'extérieur
en argument, mais c'est parfois trop restrictif.

Question 2 (fausses vérifications):

a) vérifier que la donnée a bien été transmise par la méthode POST sous
prétexte qu'elle vient d'un formulaire.
Contournement : il suffit d'envoyer une donnée vérolée par post, que ce
soit en modifiant du html ou en utilisant la librairie CURL par exemple
pour de l'attaque massive. C'est le contenu de la donnée qu'il faut
vérifier, pas son mode de transmission.

b) Vérifier que les données viennent bien "de mon site" en utilisant
HTTP_REFERRER.

Une idée (qui n'est pas de moi) et que je n'ai pas réussi à mettre en
oeuvre : injection SQL par des entiers ou plus généralement injection
SQL insensible aux habituelles vérifications sur les quotes.

Soit la requête : "UPDATE .... WHERE id=$i " avec id de type entier
(typiquement : autoincrement)
But de la manip : injecter dans $i une chaîne transformant la requête en
(par exemple):
"UPDATE ... WHERE id=0 OR 1=1"
(requête qui va corrompre les données en impactant tous les rangs de la
table)
Moyen sous mysql : utiliser la fonction mysql CHAR et complèter la
chaîne en hexa. Mais je n'ai pas réussi à le faire, je me prends ou du
syntax error ou une chaîne non interprêtée.

Espérant faire avancer le shimili... le shcibi... le biniou.

JG

10 réponses

8 9 10 11 12
Avatar
Nicolas George
Nicob wrote in message
:
J'apprécie vos certitudes. Mais jetez donc un oeil à cette page :
http://oz-formation.nexenservices.com/wholetrack/modules/news/article.php?item_id5


Effectivement, j'ai été un peu vite. En résumé, cette page racconte que des
gus ont laisser injecter le caractère " dans des attributs d'éléments HTML
délimités, justement, par ", ce qui permet d'ajouter d'autres attributs, en
particulier des attributs styles et onclick.

Dans l'absolu, ça ne change pas mon propos : l'échappement en entités HTML a
été fait en dépit de la structure du HTML, produisant du HTML
potentiellement invalide. Le patch proposé à la fin (remplacer onclick par
on*click) montre qu'en plus l'auteur de cette page n'a rien compris au
problème, puisque la bonne réponse aurait été simplement de remplacer " par
son expression correcte dans ce contexte, à savoir "¹.

Je me souviendrai donc : ne jamais utiliser PHP-Nuke.

Je tiens d'ailleurs à souligner que la solition que j'ai préconisée
ailleurs, à savoir utiliser pour la construction du résultat un arbre DOM
manipulé par une bibliothèque XML, n'aurait en aucun cas été victime d'un
tel problème.

De tout ce thread, je reste assez sidéré de constater à quel point des
programmeurs de projets de grande envergure et largement utilisés peuvent
coder au petit bonheur la chance sans comprendre réellement ce qu'ils
manipulent. Et encore : il s'agit de projets libres, soumis à la revue par
les pairs et avec des objectifs de qualité plus que de délais. Je n'ose pas
imaginer ce à quoi peut ressembler un logiciel propriétaire...

[J'espère que ce dernier paragraphe ne va pas paraître trop trollesque aux
modérateurs, que je salue au passage.]


1 : de plus, vérifier que les champs entrés sont syntaxiquement corrects,
par exemple qu'une URI respecte la syntaxe définie par la RFC ad-hoc, serait
une mesure tout à fait souhaitable.

Avatar
Nicob
On Fri, 10 Dec 2004 05:11:28 +0000, Nicolas George wrote:

Le patch proposé à la fin (remplacer onclick par on*click) montre
qu'en plus l'auteur de cette page n'a rien compris au problème, puisque
la bonne réponse aurait été simplement de remplacer " par son
expression correcte dans ce contexte, à savoir "


Ce remplacement aurait été la bonne solution dans *ce* cas précis.

Supposons que sur une autre page, un autre développeur affiche un lien à
partir des infos donné par le membre, et fait ceci :

<a href='http://$url'>

Ici, ce sera la simple quote qu'il faudra traiter. Sur une autre page :

<a href=http://$url>

Et ce coup-ci, les espaces. Donc cette approche est à mon avis très
dangereuse, il vaut mieux autoriser ce qu'on estime non dangereux et c'est
tout. Si le champ n'est pas conforme, on ne fait pas de traitement de
"protection" (passage en &quote, ...) car trop dangereux. On se contente
de logguer et de sortir "proprement".

1 : de plus, vérifier que les champs entrés sont syntaxiquement
corrects, par exemple qu'une URI respecte la syntaxe définie par la RFC
ad-hoc, serait une mesure tout à fait souhaitable.


Solution dangereuse elle aussi ! Il faut à mon avis soit :

- valider selon le fonctionnement de la *cible* (ici le browser) et non
pas selon la RFC (compliqué si on a plusieurs types de browsers
supportés ou si certains sont closed-source)

- re-générer les documents à la volée, en les analysant et en en
faisant un document valide au sens de la RFC (le risque est déporté vers
le code faisant ce traitement : saturation de ressources, erreurs dans le
parsing, ...)


Nicob

Avatar
Nicolas George
Nicob wrote in message
:
Ce remplacement aurait été la bonne solution dans *ce* cas précis.


Oui, parce que c'est *ce* cas précis qu'on me présente. Le cas général, je
l'ai déjà donné plusieurs fois : utiliser les règles syntaxiques du HTML
pour produire du HTML valide. C'est assez facile, ça marche à tous les
coups, et ça a d'autres avantages que la sécurité.

Supposons que sur une autre page, un autre développeur affiche un lien à
partir des infos donné par le membre, et fait ceci :

<a href='http://$url'>

Ici, ce sera la simple quote qu'il faudra traiter. Sur une autre page :

<a href=http://$url>

Et ce coup-ci, les espaces.


Oserais-je dire que c'est du pinaillage sans intérêt ? Oui, j'ose : c'est du
pinaillage sans intérêt.

Si les gus qui écrivent le machin ne sont même pas fichus de se mettre
d'accord sur une politique de protection du HTML cohérente, je suis bien
persuadé qu'il y aura largement pire que des cross-site-scriptings dans le
résultat.

Donc cette approche est à mon avis très
dangereuse, il vaut mieux autoriser ce qu'on estime non dangereux et c'est
tout. Si le champ n'est pas conforme, on ne fait pas de traitement de
"protection" (passage en &quote, ...) car trop dangereux. On se contente
de logguer et de sortir "proprement".


Non, ce n'est pas dangereux. C'est totalement et parfaitement sûr, aussi
bien d'après les standards que selon le fonctionnement des navigateurs un
tant soit peu répandus.

Ce qui est dangereux, c'est de mettre devant un clavier pour coder une
application en milieu hostile quelqu'un qui se croit développeur web parce
qu'il a lu « PHP pour les nuls »¹, mais qui ne comprend pas ce qu'il fait.
Ça, oui, c'est dangereux.

1 : de plus, vérifier que les champs entrés sont syntaxiquement
corrects, par exemple qu'une URI respecte la syntaxe définie par la RFC
ad-hoc, serait une mesure tout à fait souhaitable.
Solution dangereuse elle aussi !



Certainement pas, puisqu'elle rétrécit les possibilités d'une situation déjà
parfaitement sûre.


Je reprends mon discours depuis le début. La question est : comment empêcher
que des données envoyées par un utilisateur hostile ne soient transmises à
un autre client comme données malicieuses. Cette question a en fait deux
aspects, qui ont été mélangés, ce qui a nuit au propos :

1. Comment, techniquement, protéger les données ?

2. Comment éviter qu'un programmeur inattentif oublie cette protection ?

La réponse au 1, j'oserais même dire : l'unique réponse est : il suffit de
coder les caractères actifs du HTML par les entités correspondantes. Les
caractères actifs sont <, >, &, ainsi que, dans le contexte d'un attribut,
le guillemet qui délimite cet attribut.

Pour le point 2, il y a plusieurs réponses. Celle que je préconise est
d'utiliser une bibliothèque XML pour manipuler un arbre DOM.


L'objection formulée au début de ce message est une objection au point 2.
Elle ne remet pas en cause ma réponse au point 1 : il suffit de protéger les
caractères actifs, ni plus ni moins.


1 : ouvrage qui, s'il existe, est peut-être très bon, je ne me prononce pas.


Avatar
Simon Marechal
Nicolas George wrote:
Je reprends mon discours depuis le début. La question est : comment empêcher
que des données envoyées par un utilisateur hostile ne soient transmises à
un autre client comme données malicieuses. Cette question a en fait deux
aspects, qui ont été mélangés, ce qui a nuit au propos :

1. Comment, techniquement, protéger les données ?

2. Comment éviter qu'un programmeur inattentif oublie cette protection ?

La réponse au 1, j'oserais même dire : l'unique réponse est : il suffit de
coder les caractères actifs du HTML par les entités correspondantes. Les
caractères actifs sont <, >, &, ainsi que, dans le contexte d'un attribut,
le guillemet qui délimite cet attribut.


Données malicieuses != javascript pourri. C'est le cas le plus fréquent
mais aucunement le seul. On peut imaginer toute sortes de données
malicieuses. Des adresses email trop longues qui font planter le client
mail quand on clique sur un mailto: ou qui provoquent des effets
innatendus dans les scripts d'envoi d'email, des jpg pourris uploadés.
Toute donnée qui sera ensuite retraitée est à filtrer, et toutes en
seront pas traitées par un navigateur. Sans parler des champs saisis par
l'utilisateur tels que les données d'authentification, qui permettent
d'entrer lorsqu'ils sont assez longs.

Avatar
Nicob
On Mon, 13 Dec 2004 01:21:47 +0000, Nicolas George wrote:

l'unique réponse est : il suffit de coder les caractères actifs du
HTML par les entités correspondantes. Les caractères actifs sont <, >,
&, ainsi que, dans le contexte d'un attribut, le guillemet qui délimite
cet attribut.


Bon, j'ai pas vraiment le temps de faire un post super long et qui
inclurait tout ce que j'ai à dire sur le sujet. Alors je vais faire bref
et juste donner un exemple qui fout en l'air cette belle théorie.

Soit un forum où les fiches des membres contiennent (entre autres)
un lien vers une de leurs photos. Le code HTML relatif à l'inclusion de
l'image en question est le suivant :

<IMG SRC="$img">

- La variable $img est fournie par l'attaquant
- On utilise bel et bien " comme délimitateur
- Les caractères <, >, " et & sont encodés côté serveur avant affichage

Selon vous, ce code est invulnérable au XSS (cf. "il suffit de coder
..."). Selon moi, il l'est. Et c'est moi qui ait raison ;-)

Pour des exemples de contournement de filtres anti-XSS :

http://www.shocking.com/~rsnake/xss.html

Ce qui est dangereux, c'est de mettre devant un clavier pour coder une
application en milieu hostile quelqu'un qui se croit développeur web
parce qu'il a lu « PHP pour les nuls », mais qui ne comprend pas ce
qu'il fait. Ça, oui, c'est dangereux.


Pour mémoire, ça fait deux fois en quelques posts que je fous en l'air
la sécurité "définitive" que vous proposez de mettre en place.


Nicob

Avatar
John Gallet
Bonjour,

Donc cette approche est à mon avis très
dangereuse, il vaut mieux autoriser ce qu'on estime non dangereux et c'est
tout. Si le champ n'est pas conforme, on ne fait pas de traitement de
"protection" (passage en &quote, ...) car trop dangereux. On se contente
de logguer et de sortir "proprement".



Pour moi le débat n'est pas encore tellement de savoir quoi faire pour
désamorcer du code offensif dormant, mais comment le *détecter* par type
de média (osons nous restreindre à une sortie html, ça suffira bien).
Une fois qu'on sait qu'il est pourri (ou potentiellement pourri) ce sera
à chacun d'en faire ce que bon lui semble (refus, désamorçage, filtrage,
envoi par pigeon voyageur etc...). Je souhaite m'arrêter au plan
purement technique, éventuellement par type d'intervenant. Pour le
reste, un ensemble de personnes ça commence au nombre de deux, et comme
il est déjà parfois compliqué d'obtenir de certains qu'ils soient
cohérents avec eux-mêmes, cohérents avec les autres, c'est pas gagné,
donc cette partie du problème (gestion de la sécurité en équipe), on
peut la zapper.

Je reprends mon discours depuis le début. La question est : comment empêcher
que des données envoyées par un utilisateur hostile ne soient transmises à
un autre client comme données malicieuses. Cette question a en fait deux
aspects, qui ont été mélangés, ce qui a nuit au propos :


Oui.

1. Comment, techniquement, protéger les données ?
2. Comment éviter qu'un programmeur inattentif oublie cette protection ?


Ok pour ce plan de bataille.

La réponse au 1, j'oserais même dire : l'unique réponse est : il suffit de
coder les caractères actifs du HTML par les entités correspondantes. Les
caractères actifs sont <, >, &, ainsi que, dans le contexte d'un attribut,
le guillemet qui délimite cet attribut.


Est-ce vraiment suffisant, là est la question. Pour un client de type
navigateur, ça m'a l'air de réduire déjà pas mal de choses, mais
jusqu'où ?

Pour le point 2, il y a plusieurs réponses. Celle que je préconise est
d'utiliser une bibliothèque XML pour manipuler un arbre DOM.


La raison pour laquelle j'ai laissé ce bout du thread en vrac est que je
n'arrive pas à voir comment l'utiliser en ce sens. Pour moi, ça ne fait
que repousser le problème. J'aurais un joli
<MES_DATA>gnagna<SCRIPT>....</SCRIPT>gnagnagna</MES_DATA> et avec ça je
ne serai pas plus avancé après qu'avant. Quand je vais balcner le
contenu du noeud MES_DATA, le reste risque de partir avec, me
semble-t-il. J'ai aussi du mal à maitrîser l'aspect performances de la
validation temps réel de la validité du xml. Reprenons le flux de
l'information : je reçois ma requête http, je filtre un peu mes
variables, je fais ma requête SGBD, je construis un flux DOM-XML, je le
valide dans la joie et maintenant je veux envoyer du HTML qui ne
nécessite pas la dernière version (bugguée) d'IE pour être lisible, le
tout pour un site s'approchant du temps réel (au sens temps newtonien,
tiens, au hasard des cours boursiers, ou bref, n'importe quoi que je ne
peux pas mettre en cache de données).

a++
JG


Avatar
Nicolas George
Nicob wrote in message
:
Pour des exemples de contournement de filtres anti-XSS :

http://www.shocking.com/~rsnake/xss.html


J'ai lu. La moitié de ces attaques sont neutralisées par un échappement
correct des caractères actifs. L'autre moitié est hors de propos ici, car
elle ne concerne pas le même niveau.

Cette histoire de niveau est un peu difficile à comprendre, je vais essayer
de l'expliquer avec une analogie outrée. Ça répond d'ailleurs également au
message de Simon Marechal à côté dans le thread.

Supposons un forum, où on met quelque chose comme « <p>$message</p> », où le
serveur s'est assuré que $message ne contenait que des lettres, des espaces,
des virgules et des points. Parfaitement sûr, n'est-ce pas ? Et pourtant...
Avez-vous lu _Curtain_ (_Poirot quitte la scène_), d'Agatha Christie ?
(Attention, SPOILER !) Ce roman met en scène un individu qui, au moyen de
remarques innocentes placées dans la conversation, pousse certaines
personnes au meurtre. Eh bien $message a beau être épuré de tout caractère
offensif, ce forum pourrait parfaitement être utilisé par cet individu pour
commettre ses forfaits.

Ce qu'il faut bien comprendre est que le HTML est en lui-même bel et bien
totalement sûr. C'est son _contenu_ qui ne l'est pas. Et ce sont deux choses
bien différentes, et qu'il faut distinguer si on souhaite faire des
programmes sûrs. Pour tout contenu, il est possible de constituer un code
HTML qui transporte fidèlement ce contenu : c'est l'échappement des
caractères actifs en entités qui le permet.

Le contenu lui-même peut être offensif, mais c'est une autre histoire, un
jeu totalement différent et indépendant de tests, et ça n'a pas été mon
propos (à part une vague note de bas de message).

Avatar
Nicolas George
John Gallet wrote in message :
Pour moi le débat n'est pas encore tellement de savoir quoi faire pour
désamorcer du code offensif dormant, mais comment le *détecter* par type
de média (osons nous restreindre à une sortie html, ça suffira bien).


Ah, ça change tout, parce qu'au contraire, j'ai traité uniquement de la
question : comment encoder un contenu accepté de manière à ce qu'il soit
interprété comme tel, et pas comme du contenu offensif. En d'autres termes,
mon propos était d'indiquer comment faire pour que si un visiteur tape dans
un formulaire :

<script>system("rm -rf /")</script>

alors ce texte va s'afficher tel quel chez les destinataire, et pas
provoquer des actions incongrues.

Est-ce vraiment suffisant, là est la question. Pour un client de type
navigateur, ça m'a l'air de réduire déjà pas mal de choses, mais
jusqu'où ?


Les standards du web nous disent que c'est sûr. Après, il est possible que
tel ou tel client ait un bug à ce niveau, mais je n'en ai jamais entendu
parler, et j'ai tendance à penser que ce genre de bug serait de toutes
façons repéré bien avant la publication du client en question.

S'il y a dans la nature un client qui a effectivement un bug à ce niveau, on
peut envisager de contourner spécifiquement pour, en fonction de la nature
exacte du bug. Mais sinon, je ne vois pas de raison de s'en préoccuper plus
que d'un client qui, voyant « PLEASE ERASE ALL » dans une page,
obtempérerait.

La raison pour laquelle j'ai laissé ce bout du thread en vrac est que je
n'arrive pas à voir comment l'utiliser en ce sens. Pour moi, ça ne fait
que repousser le problème. J'aurais un joli
<MES_DATA>gnagna<SCRIPT>....</SCRIPT>gnagnagna</MES_DATA> et avec ça je
ne serai pas plus avancé après qu'avant. Quand je vais balcner le
contenu du noeud MES_DATA, le reste risque de partir avec, me
semble-t-il.


Je suppose que la chaîne obtenue du client dans cet exemple était au départ
« gnagna<SCRIPT>....</SCRIPT>gnagnagna », n'est-ce pas ? Dans ce cas, pour
insérer ça dans l'arbre XML sous le noeud <MES_DATA>, il faut faire quelque
chose comme (c'est du perl + libxml2, rédigé à la volée) :

my $mes_data = $document->createElement("MES_DATA");
$mes_data->appendText($client_text);

Or la méthode appendText remplacera convenablement les caractères actifs.
L'aspect important de la méthode est que pour passer du monde « chaîne de
caractères » au monde « arbre XML », il faut impérativement passer par les
méthodes de la bibliothèque, et que ces méthodes sont en nombre relativement
restreint, intensivement testées, et font toutes les validations
nécessaires. Comme les données offensives sont toutes, originalement, dans
le monde « chaîne de caractères » et que le seul point de sortie est dans le
monde « arbre XML », on a la garantie que toutes ces données offensives
passent à un moment ou à un autre à la validation.

J'ai aussi du mal à maitrîser l'aspect performances de la
validation temps réel de la validité du xml.


J'avoue que je ne me suis pas posé la question, je n'ai jamais eu à gérer un
environnement de lourde charge. Ceci dit, cette méthode sert à prévenir les
erreurs des programmeurs. Si elle est trop lourde, je vois deux solutions
assez évidentes :

- investir dans des serveurs plus puissants ;

- investir dans des programmeurs plus compétents.

Les deux coûtent, évidemment. Mais après tout, il faut bien se donner les
moyens de ses objectifs.

Je vais me laisser aller à spéculer : d'un point de vue vitesse de
traitement, je ne pense pas qu'une bibliothèque XML bien faite apporte une
surcharge si importante. Elle fera probablement quelques validations de
données déjà sûres, ou des recopies de chaînes en trop, mais je ne pense pas
que ce soit énorme. Même : la structure en arbre peut apporter un gain
considérable par rapport à l'utilisation abondante de concaténations de
chaînes dans un langage qui ne sait faire ça que par recopie complète.

Du point de vue de l'occupation mémoire, ça peut être nettement plus
coûteux, j'ai du mal à évaluer à quel point.

Il paraît que la libxml2 est particulièrement rapide et légère. Je n'ai pas
fait de mesures personnellement.

Avatar
John Gallet
Re,


Pour moi le débat n'est pas encore tellement de savoir quoi faire pour
désamorcer du code offensif dormant, mais comment le *détecter* par type
de média (osons nous restreindre à une sortie html, ça suffira bien).
Ah, ça change tout,



Pas tant que ça : je prends un exemple simpliste, disons que la chaîne
<script> est considérée comme le seul début possible de code offensif
alors il suffit de détecter cette chaîne pour savoir qu'on a du code
pourri. Libre à chacun derrière de coller du htmlentities() pour s'en
débarrasser, de faire un coup de regexp, bref, chacun sa méthode.


parce qu'au contraire, j'ai traité uniquement de la
question : comment encoder un contenu accepté de manière à ce qu'il soit
interprété comme tel, et pas comme du contenu offensif. En d'autres termes,
mon propos était d'indiquer comment faire pour que si un visiteur tape dans
un formulaire :
<script>system("rm -rf /")</script>
alors ce texte va s'afficher tel quel chez les destinataire, et pas
provoquer des actions incongrues.


Et ce faisant tu définis que les caractères offensifs sont dans les
séquences <script> et </script> et c'est ça qui me semble être le plus
important.

Est-ce vraiment suffisant, là est la question. Pour un client de type
navigateur, ça m'a l'air de réduire déjà pas mal de choses, mais
jusqu'où ?
Les standards du web nous disent que c'est sûr. Après, il est possible que

tel ou tel client ait un bug à ce niveau, mais je n'en ai jamais entendu
parler,


Amen. J'ai du mal à y croire, mais je n'ai aucun contre-exemple, ce doit
être mon esprit tordu et parano ;-)

et j'ai tendance à penser que ce genre de bug serait de toutes
façons repéré bien avant la publication du client en question.


Là en revanche je crains que tu ne frises la crise d'optimisme forcené,
si les applications étaient testées avant d'être mises en production, ça
se saurait ;-))


Je suppose que la chaîne obtenue du client dans cet exemple était au départ
« gnagna<SCRIPT>....</SCRIPT>gnagnagna », n'est-ce pas ?
Toutafé.


my $mes_data = $document->createElement("MES_DATA");
$mes_data->appendText($client_text);
Or la méthode appendText remplacera convenablement les caractères actifs.


Gasp. Mais c'est une horreur ce truc. De quel droit cette méthode àla
con s'abroge-t-elle le droit de tripoter mes données ? Enfin bon c'est
un autre soucis.
Ok donc en fait le désamorçage se fait parce que les caractères actifs
du HTML/scripting offensif sous html sont en première approche les mêmes
que ceux du XML et parce que la méthode "kivabien" purge.
D'accord, là je comprends mieux.

Comme les données offensives sont toutes, originalement, dans
le monde « chaîne de caractères » et que le seul point de sortie est dans le
monde « arbre XML », on a la garantie que toutes ces données offensives
passent à un moment ou à un autre à la validation.


Ok, on se fout que ce soit un format intermédiaire et que ce soit du
XML, on se sert seulement des fonctions de validation. Vu pour ceci.

- investir dans des serveurs plus puissants ;
- investir dans des programmeurs plus compétents.
Les deux coûtent, évidemment. Mais après tout, il faut bien se donner les
moyens de ses objectifs.
:-))) La première solution coutera bien moins cher à court terme que la

seconde, à long terme, ça reste à voir.


Bon, j'ai encore envie de faire quelques tests de mon côté, j'ai encore
une lampe rouge allumée concernant le désamorçage du code offensif (je
pense en particulier à des trucs genre aspx ou activeX) mais j'ai
l'impression qu'on a fait le tour de pas mal de choses. Maintenant il
faut que j'organise la synthèse de tout le thread...

a++
JG


Avatar
Nicolas George
John Gallet wrote in message :
Pas tant que ça : je prends un exemple simpliste, disons que la chaîne
<script> est considérée comme le seul début possible de code offensif
alors il suffit de détecter cette chaîne pour savoir qu'on a du code
pourri. Libre à chacun derrière de coller du htmlentities() pour s'en
débarrasser, de faire un coup de regexp, bref, chacun sa méthode.


Oui, mais non, il ne faut pas raisonner dans ce sens, c'est courir au trou
par oubli d'un cas. Ensuite, mon propos, c'est qu'il faut raisonner par
couches :

1. Quels contenus dois-je autoriser ?

Ex. 1 : pour un forum mathématique, il est impératif d'autoriser
n'importe quel texte, y compris contenant les caractères < et >. En
revanche, il peut être loisible de rejeter les gros mots, par exemple.

Ex. 2 : pour une URL, on accepte une URL bien formée, et uniquement sur
certains schémas (http:, oui, https:, ftp:, d'accord, javascript: non).

2. Une fois le contenu fixé, comment le coder pour qu'il soit interprété
comme tel à l'arrivée, et pas autrement (en particulier comme du contenu
offensif).

L'avantage de ce découpage, c'est que la réponse aux deux questions
individuellement est facile, et que l'interaction entre les deux est
également simple.

Au contraire, si on raisonne sur les deux à la fois, on se retrouve avec des
interactions complexes qui permettent à du code offensif de l'un des aspects
de se cacher grâce au fonctionnement de l'autre.

Et ce faisant tu définis que les caractères offensifs sont dans les
séquences <script> et </script> et c'est ça qui me semble être le plus
important.


Non, même pas. C'est ça la beauté de la chose : il n'y a pas à définir de
code offensif. Ce que je cherche à garantir, c'est que si on veut écrire
FOO, on va effectivement écrire FOO, et pas exécuter un bout de FOO, quel
que soit FOO.

Là en revanche je crains que tu ne frises la crise d'optimisme forcené,
si les applications étaient testées avant d'être mises en production, ça
se saurait ;-))


Eh bien trouvons un navigateur (autre que fait spécialement pour l'occasion
pour le faire mentir) qui ait ce problème-là précisément, et on pourra voir.
En attendant, je ne vois vraiment aucune raison de continuer à parler dans
le vide.

Gasp. Mais c'est une horreur ce truc. De quel droit cette méthode àla
con s'abroge-t-elle le droit de tripoter mes données ? Enfin bon c'est
un autre soucis.


D'une part, c'est précisément ce qu'on lui demande de faire, ce serait
dommage qu'elle ne le fasse pas. D'autre part, elle ne tripote pas les
données, elle se contente de les écrire de la façon convenable, suivant un
mécanisme parfaitement réversible.

Ok donc en fait le désamorçage se fait parce que les caractères actifs
du HTML/scripting offensif sous html sont en première approche les mêmes
que ceux du XML et parce que la méthode "kivabien" purge.
D'accord, là je comprends mieux.


Ce n'est pas exactement ça, et en plus je me suis mal exprimé. Je vais
reprendre.

J'ai à ma gauche du texte fourni par un utilisateur, texte potentiellement
offensif, ou maladroit, ou simplement qui écrire « a<b » sans que <b peut
avoir un sens en HTML. Informatiquement, c'est une chaîne de caractères.
J'ai à ma droite le navigateur d'un visiteur, sur lequel je veux envoyer une
page exprimant le contenu que j'ai à ma gauche. Informatiquement, il va
falloir une chaîne de caractères, qui exprime des choses en HTML.

La manière de procéder, une fois que le contenu lui-même est considéré comme
valide, est connue et simple : c'est le codage sous forme d'entités.

Toute cette partie de mon histoire ne concerne que la question : comment
éviter qu'un programmeur étourdi fasse malencontreusement passer une chaîne
de gauche à droite sans passer par les fonctions d'encodage convenable. Il y
a plusieurs réponses. Perl, par exemple, a le mécanisme du taint mode, qui
pourrait être utilisé ici : les chaînes de gauche sont marquées comme
tainted, et les fonctions d'envoi au navigateur rejettent les chaînes
tainted.

Mon histoire de bibiliothèque XML DOM est une autre méthode pour ça. L'idée
est la suivante : définissons la structure de données page_web, qui contient
une représentation interne d'une page web. Définissons la fonction
envoyer_page(page_web w), qui prend en argument un objet de type page_web,
et l'envoit au client correctement encodée. Puis fermons tous les autres
canaux pouvant conduire au client. Enfin, définissons les fonctions
nécessaires, peut-être ajouter_texte, passer_en_gras, pour construire des
objets de type page_web.

On se retrouve avec quelque chose du genre :

.' ' ' ' ' '. |#|
: : |#|
: données : |#| socket
: non-fiables : chaînes de caractères |#| reliée au
: : |#| -------------->
'...........' |#| client
_._._._._._._._._._._._ /_._._ /_._._._._._._._|#|
/ / |#|
(1) (2) |#|(3)
objets page_web >===<
|#|
|#|

(1) = fonction ajouter_texte
(2) = autre fonction de construction de page_web
(3) = fonction envoyer_page


Avec une telle structure, on est certain qu'une chaîne de caractère
non-fiable, pour parvenir jusqu'à la socket reliée au client, doit
impérativement passer par la fonction envoyer_page. Dès lors, il suffit de
rédiger cette fonction avec grand soin pour que l'encodage de la page web
soit correct.

Le HTML ayant une structure arborescente, il est assez naturel de prendre
une structure d'arbre pour le type page_web. Par exemple, en Caml
(particulièrement bon pour manipuler les arbres), on pourrait écrire quelque
chose comme :

type page_web | Element of string * page_web list
| Text of string

Une page web triviale s'écrirait :

Element ("html", [
Element ("head", [ Element ("title", [ Text "Hello, world!" ]) ]);
Element ("body", [
Element ("p", [
Text "Hello, ";
Element ("b", [ Text "world" ]);
Text "!"
]);
])
])

La fonction envoyer_page s'écrirait quelque chose comme :

let envoyer_page page_web let rec string_of_page_web = function
| Text t -> encode_entites t
| Element (e, c) ->
"<" ^ e ^ ">" ^
(List.concat (List.map string_of_page_web c)) ^
"</" ^ e ^ ">" in
output_string client_socket (string_of_page_web page_web)

Ce qui, sur mon exemple trivial, donnerait :

<html><head><title>Hello, world!</title></head><body><p>Hello,
<b>world</b>!</p></body></html>

Bref, tout ce qu'il y a à faire, c'est écrire cette petite bibliothèque de
manipulation interne de page web sous forme d'arbre en mémoire.

Mais on peut remarquer ceci : la dernière mouture du HTML, le XHTML, est du
XML. Et des bibliothèques qui font cette manipulation d'arbres en mémoire et
sortent du XML au final, il y en a déjà qui font très bien le boulot. Autant
les utiliser, d'autant que c'est quand même un peu plus complexe que ce que
j'ai écrit, à cause des attributs, des histoires d'encodage et de
l'espacement (ce n'est pas une bonne idée d'envoyer une page web qui fait
une seule gigantesque ligne, des vieux navigateurs apprécient mal).

Au final, on a donc deux garanties :

- le contenu qu'on a accepté passe forcément par la fonction de sortie, qui
assure que l'interprétation comme HTML sera fidèle et n'ajoutera pas
d'interprétation intempestive, dommageable du point de vue de la
sécurité ;

- la sortie est du XHTML parfaitement valide, ce qui rend le fonctionnement
des feuilles de styles et des scripts (ceux fournis par le serveur, dûment
autorisés) bien plus fiable, ce n'est pas à négliger.

Attention, on n'a PAS la garantie suivante :

- le contenu lui-même est inoffensif.

Dans le cas de texte, il n'y a pas de risque technique (mais des risques
« humains », si on souhaite donner dans le politiquement correct, cf. mon
exemple mi-sérieux au début au sujet d'un filtrage des gros mots.

Dans le cas d'attributs ayant une signification plus technique, et au
premier plan les URL, il faut faire des vérifications. Ces vérifications
doivent êtres faites :

- en amont de l'insertion dans l'objet page_web ;

- au cas par cas en fonction du type de contenu (on ne valide pas une URL de
la même manière qu'on valide une couleur CSS.

Éviter les risques d'oubli de ces validations passe probablement par
l'encapsulation des fonctions de la bibliothèque DOM dans des fonctions qui
à leur tour font ces validations.

Pour la question du coût en puissance, je n'en sais vraiment rien, et des
données fiables m'intéressent.

8 9 10 11 12