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

Avatar
John Gallet
Bonjour,


J'ai vraiment du mal à voir le problème : tous les langages raisonnables
sont capables de manipuler des chaînes de caractères « inertes », qui ne
provoqueront pas d'exécution surprise. C'est le cas du HTML par remplacement
de <, > et & par des entités nommées ; c'est le cas de PostgreSQL (les
champs de type text, char, varchar, et les blob), et j'ose espérer que les
autres gestionnaires de bases de données sont dans le même cas ; c'est le
cas de perl (une variable de type scalaire), et j'ose espérer que PHP ne
diffère pas là-dessus.


Si j'ai bien compris le principe des attaques de type xss, il y a deux
types : immédiate (on passe des arguments à la con et ça fout tout de
suite le boxon) ou "à retardement" / stockage c'est à dire qu'on va
stocker du code offensif en SGBD dans une zone qui sera relue plus tard
et là le code offensif sera lancé.
Ce que je me demandais, c'est comment filtrer avant le code offensif
*avant de le stocker* pour le "désamorcer" et éviter qu'il ne soit
exécuté plus tard quand quelqu'un le récupèrera "à l'insu de son plein
gré". Au passage, ça devrait me gérer aussi un certain nombre d'attaques
à effet instantané.

Autrement dit ma question est : comment *rendre* inerte un code
offensif, dont je ne connais pas la nature par définition (JS, VB, aspx,
active-X, applet-java, cobol-objet embarqué déguisé en concombre masqué,
etc...) au plus tôt c'est à dire avant de le stocker, pour que jamais il
ne puisse être exécuté plus tard, ni dans nu navigateur, ni dans une
autre application.

Donc pour répondre au cas évoqué plus haut : si dans un formulaire
l'utilisateur tape « <script type='text/javascript'> » on reçoit
« %3cscript+type=%27text%2fjavascript%27%3e » (si c'est un GET), qu'on
transforme immédiatement en « <script type='text/javascript'> » parfaitement
inerte. Pour l'envoyer au serveur de bases de données, on l'écrit
« '<script type='text/javascript'>' » (car < et > ne sont pas des
caractères spéciaux pour SQL, mais ' l'est), qui le décode assez rapidement
en « <script type='text/javascript'> » (toujours la même chose, toujours
inerte) et le stocke.


On le stocke et donc quand on le demande à la base elle va bien renvoyer
du code JS valide n'est-ce pas ? C'est justement ça que je veux éviter.

Quand on consulte la valeur, après décodage de la réponse SQL, on obtient à
nouveau la chaîne inerte « <script type='text/javascript'> ». Et si on veut
l'envoyer dans une page web, on encode ça en « &lt;script
type='text/javascript'&gt; », rien de plus.


Oui mais non, parce qu'il faut penser à le faire dans TOUTES les
applications qui accèdent à la base. Si je stocke directement une bombe
désamorcée, il y a moins de chance qu'elle me p... à la g... quand je la
tripote plus tard que si je la stocke en vrac et charge au tripoteur de
faire le désamorçage, non ?

Donc ça répond partiellement à ma question : pour désamorcer du code
offensif de type JS, il suffit de ne stocker que des htmlentities dans
la base. Maintenant quid d'autres langages de scripting ? Est-ce que ce
brave IE (au hasard....) ne serait pas capable de recoller les morceaux
si on lui donne un activeX sous une forme ou sous une autre qui serait
immunisée à une transformation en html entities ?

Mais
idéalement, il faudrait _quand même_ décomposer ça suivant la grammaire du
sous-ensemble de HTML choisi, et recomposer ensuite le HTML à l'envoi.


Donc si je comprends bien : ou on utilise des balises "propriétaires" ou
pas (en les calquant par exemple sur un autre langage incompatible avec
html), mais dans tous les cas, on ne transforme pas en entités html un
certain nombre de balises qu'on a spécifiquement autorisées. Autrement
dit, on ne peut pas passer tout le texte à une transformation
htmlentities, il faut le faire caractère par caractère, selon le
contexte.

_peut_ même s'en sortir (par exemple avec un traitement par expressions
rationnelles qui du style s{<(?!/?(:?em|b)>)/&lt;/g (c'est crade, ça laisse
traîner les >, ce qui est légal il me semble mais pas beau ; ne pas
réutiliser ce code en vrai !) pour n'autoriser que <em>, </em>, <b> et </b>
et rendre inerte toute autre balise). Mais c'est casse-figure.


Pourquoi est-ce casse figure ? Parce qu'on utilise des balises HTML au
lieu de balises propriétaires ? Je ne vois pas le risque.

a++;

JG

Avatar
Nicolas George
John Gallet wrote in message :
Autrement dit ma question est : comment *rendre* inerte un code
offensif, dont je ne connais pas la nature par définition (JS, VB, aspx,
active-X, applet-java, cobol-objet embarqué déguisé en concombre masqué,
etc...)


Ça dépend de ce à quoi il est envoyé. Si c'est envoyé comme HTML, alors le
codage comme entités est nécessaire et suffisant. Pour d'autres formats
d'envois, il y aura d'autres mesures à prendre.

On le stocke et donc quand on le demande à la base elle va bien renvoyer
du code JS valide n'est-ce pas ? C'est justement ça que je veux éviter.


Elle va renvoyer du _texte_ qui _si on insiste pour l'interpréter comme du
javascript_, sera effectivement du javascript valide.

Oui mais non, parce qu'il faut penser à le faire dans TOUTES les
applications qui accèdent à la base.


Évidemment. Il faut aussi penser à tester la taille de TOUS les buffers dans
lesquels on stocke des données, par exemple. C'est le B-A-BA de la sécurité.
La bonne manière de faire ça est de passer systématiquement par une
bibliothèque, écrite une fois pour toutes, et qui fait les choses comme on
le souhaite.

Il y a deux problèmes avec le désamorçage avant stockage :

- D'une part, il faut penser à le faire avec TOUTES les sources de données,
et il n'y a aucune raison que ce soit plus facile de le faire partout à la
source que de le faire partout à la cible. Le problème est le même.

- D'autre part, il n'est pas possible de faire le désamorçage pour toutes
les applications : si on désamorçe pour du HTML, ce sera moche pour du
text/plain, et ça ne protège pas d'une injection SQL. Si on protège pour
du SQL, ce sera moche en HTML. À la limite, on peut s'en sortir en codant
le tout complètement en hexadécimal, et en l'envoyant tel quel, mais la
lisibilité risque de s'en ressentir.

Non, vraiment, la _seule_ méthode viable, c'est de faire la protection au
moment où on sait par quoi sera interprété ce qu'on va envoyer.


Est-ce que ce
brave IE (au hasard....) ne serait pas capable de recoller les morceaux
si on lui donne un activeX sous une forme ou sous une autre qui serait
immunisée à une transformation en html entities ?


Le HTML a une spécification, et cette spécification dit que les entités sont
inertes. Après, il y a des bugs dans tous les logiciels, et surtout dans
internet explorer. On peut vouloir faire du bugware pour contourner tel trou
particulièrement dangereux et qui n'est pas comblé par microsoft, mais on ne
peut nécessairement faire ça que pour les trous connus, donc c'est
fondamentalement voué à l'échec.

Donc si je comprends bien : ou on utilise des balises "propriétaires" ou
pas (en les calquant par exemple sur un autre langage incompatible avec
html), mais dans tous les cas, on ne transforme pas en entités html un
certain nombre de balises qu'on a spécifiquement autorisées.


Non, on transforme _deux fois_ : les balises propriétaires en codage
interne, puis le codage interne en balises HTML. L'une ou l'autre de ces
transformations peut être l'identité, et elles peuvent être réciproques
l'une de l'autre, mais plus on essaie d'aller vite, plus on risque de se
planter.

Pourquoi est-ce casse figure ?


Parce qu'on prend un raccourci. On risque d'oublier des cas, d'autoriser du
texte mal formé, etc. C'est comme pour résoudre une équation au lycée : on
peut faire deux étapes d'un coup, mais on multiplie ainsi les risques
d'erreurs de calcul. En décomposant totalement les opérations, et en
validant à chaque étape, on réduit considérablement les risques.

Avatar
Dominique Blas
Eric Razny wrote:


Ce source unique simplifie grandement la maintenance (tout est au même
endroit) et est plus lisible qu'une multitude de fichiers disséminés un
peu partout dont on oublie le fil à force de passer de l'un à l'autre.
Dans sécurité il y a simplicité et bon sens.


Donc c'est dans cette "simplicité" apportée par cette méthode de
développement que se trouve le "plus" de sécurité ou j'ai loupé autre
chose ?


Sur le fond je suis plutôt d'accord avec db.
Autant je m'oppose à son système d'état *si* celui ci empêche un surf
Attention j'ai bien précisé que le site doit l'accepter.

Si nous avons affaire à un bête site de pages statiques à quoi bon ?
En revanche si nous avons affaire à un site d'affaire (B to C, B to B, B to
ma mère) alors là oui.
D'un autre côté rien n'interdit au premier type de site (sites
d'informations, de présentation, de recherche) de fonctionner selon les
principes du second. Mais cela interdit le surf et la mise en bookmark
c'est clair.
Ca facilite énormément la publication côté webmestre (il se contente de
stocker les documents et de les référencer dans le SGBD ou l'annuaire et
basta, c'est le logiciel qui se démerde pour les présenter) mais côté
internaute c'est un peu la zone.
Maintenant reconnaisez que TOUS les portails fonctionnent de cette manière.
Et heureusement si en plus de stocker les documents il fallait en plus leur
trouve une place et un nom sur le système de fichiers ...

agréable autant l'idée de ne pas avoir à fouiller à droite et à gauche
pour une modif même mineure favorise la maintenance et donc (amha) la
sécurité.

(...)


Par contre (db me corrigera si je me trompe! :) ) db voit aussi un plus
sécurité sur le côté mono-état (automate) de son appli.
mono-état est faux. Inutile de parler d'automate s'il n'y a qu'un seul état.

Un seul état = une page statique.

Ce n'est pas
faux mais je pense qu'on peut faire aussi sur en posant moins de
contrainte sur l'utilisateur (par contre le concepteur a intérêt à bien
cogiter avant de se lancer!)[1]
C'est bien là un gros souci. Il est TRES TRES rare que le concepteur

maîtrise le sujet aussi bien que le client (client au sens relation
commerciale hein ?).
A moins qu'il ne s'agisse d'un gros projet (avec donc des moyens conséquents
et de nombreux jours.homme de debriefing et la pleine et entière
participation du client) il a donc beau cogiter il y aura toujours des
choses à changer, à modifier ( << Ah mince, je ne vous l'ai pas dit mais
c'est comme cela que ça fonctionne pas comme ça >>; << On vient de me dire
que la formule que je vous ai fournie est fausse, la bonne c'est celle-là
; << je suis confu, la procédure que je vous ai livrée en avant-projet
n'est plus en vigueur depuis 6 mois, c'est Chantal mon assistante qui me


l'a fait remarquer : là, on ne passe plus par le chef de rayon, il faut
prendre en compte l'aval de la direction des ventes avec émission d'un
formulaire B4, ...>>, etc ).
Bien souvent sur les petits et moyens projets c'est le client qui découvre
la modélisation de son métier durant le processus de développement. Et, bien
entendu, c'est ce même client qui a refusé dès le départ les 20 jours de
modélisation en affirmant que c'était << facile >> ou le commercial qui
n'est pas monté jusqu'à 20 jours car sinon la proposition était refusée par
le client.
Alors cogiter oui mais pas trop fort (de plus je suis paresseux).

Comme je l'ai écrit dans un post précédent, le contrôleur n'apporte pas un
gain de sécurité directement, c'est avant tout un gage de simplicité.


[paragraphe sur les exceptions]
M'enfin ce n'est pas le sujet.


Agreed.


Disagreed :) Voir plus bas

Même si en PHP je crains que ça ne conduise à de belles horreurs
car il va falloir faire la chasse aux exceptions non catchées lors des
tests unitaires (à la charge des développeurs donc) vu que ce n'est pas
un langage compilé. Faudra que je vérifie si en lançant un php -q
toto.php le moteur est capable de signaler les exceptions non catchées.


Le non traitement des erreurs (par exceptions ou pas) peut être un
facteur important d'insécurité. Il arrive (trop) fréquement qu'en
surfant sur un site on tombe sur un jouli rapport d'erreur créé par le
moteur de l'appli avec les scripts et les chemins qui vont avec (merci
pour l'info, sans compter que ça permet souvent d'identifier le
framework qu'il y a derrière) quand ce n'est pas le nom des tables, des
champs, voire un tuple complet :)
Oui, ok mais bon quand on développe on essaye d'aller jusqu'au bout de la

démarche n'est ce pas ?

Qui a prononcé le mot << budget >> dans l'assemblée ?

D'un autre côté une fois que sur un projet TOUTES les exceptions ont été
traités c'est reconductible sur un autre projet.
De mon côté je dois reconnaître que ça m'a un peu changé la vie (et le
déverminage).

db


--
email : usenet blas net



Avatar
Dominique Blas
John Gallet wrote:

(re)bonjour,

Merci pour cette explication sur les principes mis en oeuvre. Je pense
avoir compris (à peu près) le résultat, mais j'ai du mal à voir en quoi
ça apporte un "plus" de sécurité.
L'histoire du script unique (*) apporte un gain indirect.


Le fait d'avoir un seul script (*) à maintenir, facilite déjà la maintenance
(sic) puis le déverminage et accessoirement la mise en place de
points de contrôle en vue de pister.

Dans ce que j'ai écrit les gains directs sont apportés :
- par la configuration du serveur Web en vue de rediriger SYSTEMATIQUEMENT
sur la page d'accueil : cela interdit toute saisie d'un URL quelconque
(avec des ? et des & en vue d'une injection SQL par exemple) ;
- par le filtrage des entrées.

(...)
Justement c'est là que ça coince : la mesure du possible. Sans rentrer
dans un probème de confort de l'utilisateur, il y a des zones de type
"données mortes" (genre textearea stockées en TEXT ou BLOB ou je sais
pas quoi en base parce l'application en a besoin) mais dans lesquels à
part les ~ ^ et à la limite | tous les autres caractères sont
explicitement autorisés arce que l'utilisateur peut parfaitement avoir
BESOIN de les saisir.
Je sais que ce type de saisie ouverte doit être acceptée parfois (Wiki,

forums). Sans aller jusqu'à recoder un interpréteur je pense qu'une
translation est peut-être une solution.
Je m'explique. Le système de filtrage explicité auparavant intervient dès la
soumission du formulaire en éléminant les caractères inacceptables. On peut
très bien imaginer qu'au lieu de les éliminer dans le cas d'une zone
ouverte il les translate ( ex : ';' devient 0xff, '$' devient 0xfe, etc)
voire les coder sur 2 octets (UTF-8)).
Ainsi si jamais le contenu des zones est passé tel quel à un processus shell
(ce qui ne devrait jamais arrivé mais bon) ou au SGBD au moins ils seront
considérés comme faisant partie des données ou au pire cela conduira à une
erreur.

(...)

Donc tu détermines l'état courant en fonction d'un arbre de décision
(les switch et sous-switch) selon les données reçues du monde extérieur
par l'url/post. Mais en quoi est-ce que ça te blinde contre une
injection SQL quand tu dois aller faire une requête en SGBD pour
vérifier que le login et le mot de passe que tu as reçus de ton
formulaire sont corrects ? Je conçois qu'on puise y voir une propreté ou
une simplicité de codage (chacun voit midi à sa porte) mais je ne vois
pas où est la valeur ajoutée en termes de sécurité.
Voir plus haut. Cette section n'avait pas trait à la sécurité directement.

Elle était présente en réponse à ta demande d'explications sur la
cinématique de la chose.

Ce source unique simplifie grandement la maintenance (tout est au même
endroit) et est plus lisible qu'une multitude de fichiers disséminés un
peu partout dont on oublie le fil à force de passer de l'un à l'autre.
Dans sécurité il y a simplicité et bon sens.


Donc c'est dans cette "simplicité" apportée par cette méthode de
développement que se trouve le "plus" de sécurité ou j'ai loupé autre
chose ?
Entre autres oui, voir plus haut.


Le pb du premier vol d'Ariane V était, si mes souvenirs sont bons, une
consigne de vol (vitesse horizontale) qui n'avait pas été changée par
rapport à Ariane IV. Même sans gestion par exception la catastrophe était
annoncée.
Si la mienne est bonne aussi, c'était une exception mathématique non

trappée qui s'est propagée jusqu'au niveau le plus haut et là il n'y a
plus eut qu'une seule chose à faire : transformer le biniou en gros
pétard de feu d'artifice du 14 juillet.
Il serait arrivé la même chose dans le cas d'une gestion d'erreur locale si

la division par zéro n'avait pas été gérée.
A moins que tu ne veuilles signifier que l'on voit davantage les problèmes
avec une gestion locale qu'avec une gestion par exception ? Ce qui n'est
pas faux je te l'accorde.
Quoi qu'il en soit, si on avait pas fichu les consignes de vitesse
horizontale d'Ariane IV on n'aurait pas été obligé de nettoyer la zone de
chute des boosters pendant des semaines.

Pour la petite histoire ce n'est pas forcément une querelle de
presbytères mais une question de priorité. Gérer les erreurs au niveau le
plus bas c'est sans doute intellectuellement satisfaisant mais conduit
irrémédiablement à un code lourd.


Pour moi ça dépend du type d'applications et de la gravité des erreurs.
Prenons par exemple l'échec de connexion au SGBD. Dans beaucoup de cas,
et presque tout le temps si on est pas en daemon, on va de toutes façons
arrêter le programme sans autre forme de procès.
Arrêter la requête tu veux dire ? N'oublies pas que nous parlons

d'applications Web et que s'il y a un pépin sur le SGBD le serveur
d'applications lui est toujours vivant et qu'il doit donc prendre en charge
cet évènement est en avertir proprement l'internaute ainsi que
l'administrateur (éviter le message trop souvent vu sur les IIS : SQL
server Error 0x80EF000C ...(j'invente) par exemple).
Si le serveur d'applications est en panne même chose côté serveur Web en
prévoyant de remplacer l'erreur 500 par une belle page indiquant simplement
l'indisponibilité du service.
Mais tout cela n'est pas de la sécurité (encore que en évitant les messages
<< techniques >> on ne délivre pas d'informations permettant de connaître
l'environnement).
M'enfin ce n'est pas le sujet.
Agreed. Même si en PHP je crains que ça ne conduise à de belles horreurs

car il va falloir faire la chasse aux exceptions non catchées lors des
tests unitaires (à la charge des développeurs donc) vu que ce n'est pas
un langage compilé. Faudra que je vérifie si en lançant un php -q
toto.php le moteur est capable de signaler les exceptions non catchées.
C'est l'intérêt du throws de Java du reste.

Mais bon ça c'est de l'implantation.
Dans les 2 règles exposées le filtrage est évident. Le filtrage des URL,
lorqu'il est possible (lorsqu'on peut se passer des GET), est un sacré
bienfaiteur en terme de sécurité mais le site doit suivre (conservation
d'états).

db

(*) Quand je parle d'un script unique je simplifie. C'est une manière de
faire, simpliste. Les méthodes actuelles partent plutôt d'une classe unique
(classe Action de Struts par exemple ou BComponent (si mes souvenirs sont
bons) de Barracuda).
--
email : usenet blas net


Avatar
John Gallet
Re,


Le fait d'avoir un seul script (*) à maintenir, facilite déjà la maintenance
(sic) puis le déverminage et accessoirement la mise en place de
points de contrôle en vue de pister.


Le point d'entrée unique est toujours intéressant, tout à fait d'accord.

Dans ce que j'ai écrit les gains directs sont apportés :
- par la configuration du serveur Web en vue de rediriger SYSTEMATIQUEMENT
sur la page d'accueil : cela interdit toute saisie d'un URL quelconque
(avec des ? et des & en vue d'une injection SQL par exemple) ;
- par le filtrage des entrées.


Oui sans hésiter pour le filtrage des entrées. En revanche je n'arrive
pas à voir exactement pourquoi le point d'entrée unique te protège
contre une injection SQL, tu peux très bien la recevoir dans une de tes
variables POST. Je sens que je n'ai pas encore compris tous les
principes que tu mets en oeuvre dans ta notion d'automate à ce sujet.
Quand tu dois écrire ta clause WHERE, c'est bien parce que tes données
user sont filtrées que tu es protégé, pas parce qu'elles ont été
transmises en POST.

Je sais que ce type de saisie ouverte doit être acceptée parfois (Wiki,
forums). Sans aller jusqu'à recoder un interpréteur je pense qu'une
translation est peut-être une solution.
Je m'explique. Le système de filtrage explicité auparavant intervient dès la
soumission du formulaire en éléminant les caractères inacceptables. On peut
très bien imaginer qu'au lieu de les éliminer dans le cas d'une zone
ouverte il les translate ( ex : ';' devient 0xff, '$' devient 0xfe, etc)
voire les coder sur 2 octets (UTF-8)).


Si je comprends bien, au lieu de les "traduire" en leur équivalent
htmlentity tu les traduis en code interne, en espérant que l'application
qui les récupèrera et les affichera (par exemple IE dans le cas d'un
script JS offensif stocké en base) ne saura pas les comprendre après.

Voir plus haut. Cette section n'avait pas trait à la sécurité directement.
Elle était présente en réponse à ta demande d'explications sur la
cinématique de la chose.


Ok pour ceci, et merci pour les explications. Il y a encore des trucs
qui m'échappent parce que je dois être réfractaire à certains concepts,
mais rien qu'un bon bouquin ne saurait corriger (et m... il y en a déjà
4 ou 5 qui attendent que j'ai eu le temps de les lire...
bouquin_a_lire++; )

A moins que tu ne veuilles signifier que l'on voit davantage les problèmes
avec une gestion locale qu'avec une gestion par exception ? Ce qui n'est
pas faux je te l'accorde.


Oui c'est ce que j'en pense. Plus vite je gère (correctement hein, pas
en disant "m'a planté débrouillez vous") mes erreurs, moins j'ai de
chances qu'elles ne se propagent et que le code continue à s'éxécuter
selon le principe garbage-in garbage out.

Pour la petite histoire ce n'est pas forcément une querelle de
presbytères mais une question de priorité. Gérer les erreurs au niveau le
plus bas c'est sans doute intellectuellement satisfaisant mais conduit
irrémédiablement à un code lourd.
Pour moi ça dépend du type d'applications et de la gravité des erreurs.

Prenons par exemple l'échec de connexion au SGBD. Dans beaucoup de cas,
et presque tout le temps si on est pas en daemon, on va de toutes façons
arrêter le programme sans autre forme de procès.
Arrêter la requête tu veux dire ?



Pire que ça :
$dad=mysql_connect(....);
if($dad=úLSE)
{
require('std_excuse.html');
exit();
}
Je peux pas me connecter à la base, je sais que sans la base je peux pas
faire le boulot qui m'est demandé, je me casse. Je vais pas générer une
exception à gérer je sais pas où qui sera trappée ou pas par je sais pas
qui, je peux rien faire, j'arrête les frais.
Je sais, je triche, c'est un exemple qui s'y prête bien.

N'oublies pas que nous parlons
d'applications Web et que s'il y a un pépin sur le SGBD le serveur
d'applications lui est toujours vivant


Pas nécessairement, d'aucuns sont assez "doués" pour aller chercher de
temps à autres un paramètre de config ou truc du genre. Mais quoi qu'il
en soit, je vois pas pourquoi je continuerais mon programme si de toutes
façons je peux pas exécuter mon boulot.
Dans la même veine, je vais pas me priver de faire un ROLLBACK et à me
casser violemment si ma requête échoue : ou la connexion est dans les
choux (genre : équipement réseau un peu sensible qui l'a coupée parce
que temps de session dépassé) ou il y a une erreur de syntaxe (attaque
probable si j'ai bien recetté mon programme).

et qu'il doit donc prendre en charge
cet évènement est en avertir proprement l'internaute ainsi que
l'administrateur (éviter le message trop souvent vu sur les IIS : SQL
server Error 0x80EF000C ...(j'invente) par exemple).


Tout à fait, mais on peut très souvent parfaitement gérer ça directement
en local, dès la détection de l'erreur. Et le code en est *à mon sens*
grandement plus lisible et facile à débugger que de suivre les try/catch
dans tous les coins : si je reviens dans l'appelant après ma fonction,
c'est qu'elle s'est exécutée proprement et complètement, sinon elle
serait sortie. Mais bon, chacun voit la "simplicité" et la "propreté" à
sa manière. Moi je trouve lourd de me farcir des try/catch imbriqués
dans tous les coins, d'autres trouvent lourd de devoir faire un teste
systématique du code retour de toutes leurs fonctions et préfèrent
transmettre l'exception au dessus et faire le throw plus bas. Peu
importe.

Sur le fond nous sommes bien d'accord : il faut que toutes les erreurs
soient gérées pour que les données de l'application restent cohérentes,
en loggant l'erreur dans un endroit inacessible à un attaquant
potentiel, et en présentant un message d'excuses à l'internaute honnête
qui n'a pas eu de bol de tomber sur le bug.

Dans les 2 règles exposées le filtrage est évident. Le filtrage des URL,
lorqu'il est possible (lorsqu'on peut se passer des GET), est un sacré
bienfaiteur en terme de sécurité mais le site doit suivre (conservation
d'états).


Je percute vraiment pas en quoi enlever le GET est un apport de sécurité
pour le site (à part, on l'a déjà vu, pour les données sensibles type
password ou l'identifiant de session qui se fait écrire grâce au
referrer dans le log d'un attaquant). Que je reçoive la donnée vérolée
en get ou en post, elle est tout aussi vérolée. C'est pas pour faire ma
mauvaise tête, ça m'intéresse vraiment de comprendre.

a++
JG



Avatar
Nicolas George
Dominique Blas wrote in message
<41ae58be$0$30405$:
Je sais que ce type de saisie ouverte doit être acceptée parfois (Wiki,
forums). Sans aller jusqu'à recoder un interpréteur je pense qu'une
translation est peut-être une solution.
Je m'explique. Le système de filtrage explicité auparavant intervient dès la
soumission du formulaire en éléminant les caractères inacceptables. On peut
très bien imaginer qu'au lieu de les éliminer dans le cas d'une zone
ouverte il les translate ( ex : ';' devient 0xff, '$' devient 0xfe, etc)


Je suis désolé, mais j'ai envie de dire que c'est n'importe quoi.

Si je commande à un informaticien un serveur pour un forum d'entraide
mathématique (donc où il faut taper des formules), que l'informaticien me
livre le serveur, mais qu'il me dit « attention, pour des raisons de
sécurité, il n'y aura pas marqué < et > dans les textes, mais 0xFF et
0xFE », moi je vais avoir envie de lui répondre « attention, pour des
raisons de sécurité, il n'y aura pas marqué 10000 EUR sur votre chèque, mais
0x00 EUR ».

S'il est dans les spécifications d'autoriser tel ou tel caractère, il faut
l'autoriser, et la seule manière de le faire est de les remplacer par leur
forme inerte.

(Et comme cette forme inerte dépend de ce à quoi on envoit ces caractères,
il ne faut le faire qu'au moment où on sait ce qu'on va en faire.)

voire les coder sur 2 octets (UTF-8)).


On ne peut pas coder arbitrairement n'importe quel caractère sur deux octets
en UTF-8. La correspondance caractère <-> codage UTF-8 est totalement
univoque (avec caractère = codepoint Unicode).

(La construction arithmétique d'UTF-8 est équivoque, mais les
implémentations sont censées rejeter avec une erreur tout ce qui n'est pas
la représentation de longueur minimale, _surtout_ sur les données d'origine
extérieure.)

Avatar
John Gallet
Re,

(mine de rien, j'essaie de faire une synthèse, si, si)

S'il est dans les spécifications d'autoriser tel ou tel caractère, il faut
l'autoriser, et la seule manière de le faire est de les remplacer par leur
forme inerte.
(Et comme cette forme inerte dépend de ce à quoi on envoit ces caractères,
il ne faut le faire qu'au moment où on sait ce qu'on va en faire.)


Est-ce que je résume ton point de vue en disant ceci :

Il faut rendre inerte du code offensif pouvant potentiellement être
dangereux lors de sa relecture ultérieure par une application. Comme les
trsnformations nécessaire à le rendre inerte dépendent de l'application
(par exemple : transformer en entités html pour un navigateur ou
[INSERER ICI UN AUTRE EXEMPLE DANS UNE AUTRE APPLI] il est préférable de
faire ces traductions quand on récupère les informations par type de
canal de sortie.

Auquel j'ajouterai :

Si on a un seul canal de sortie (par exemple, l'interface html de
l'administrateur) on peut aussi faire la transformation avant le
stockage.

En fait ce qui me gêne dans le "désamorçage a posteriori" c'est que je
suis quasi certain qu'à un moment ou à un autre, "on"(1) va oublier de
le faire. Alors que si je fais le déminage tout de suite, je suis
peinard.

a++
JG
(1) ici, "on" ça peut être le développeur fatigué, le nouveau venu dans
l'équipe de développement, etc... Même avec des belles
fonctions/méthodes d'accès aux données, je craisn qu'il n'y ait une
faille un jour.

Avatar
Nicolas George
Nicolas George wrote in message <cokpko$1bl0$:
La bonne manière de faire ça est de passer systématiquement par une
bibliothèque, écrite une fois pour toutes, et qui fait les choses comme on
le souhaite.


Tiens, je me réponds à moi-même parce que j'ai repensé à ça, et q'il y a un
point que j'ai oublié de mentionner : il y a une méthode pour avoir une
garantie fiable de ne jamais envoyer à la sortie de HTML (par exemple)
non-sûr : faire en sorte que les seules fonctions reliées directement à la
sortie n'acceptent pas de chaînes de caractères simples, mais uniquement des
valeurs/objets d'un type abstrait HTML, valeurs qui ne peuvent être
construites que par des appels de constructeurs au nom évocateur.

Ainsi, si « print $string » n'envoit pas $string au client mais simplement
dans les logs, et que pour envoyer quelque chose au client, il faut appeler
« output $html », avec $html qui ne peut être construit, au choix, qu'avec
html_escape_from_string ou html_unsafe_import_from_string, les problèmes
d'injection de codent se voient comme le nez au milieu de la figure : il y a
marqué « unsafe » dessus.

Dans le cas du HTML, il y a d'ailleurs un choix assez avantageux du type
abstrait à utiliser pour ça : un arbre XML manipulé par une bibliothèque
DOM. Ainsi, on a non seulement la garantie d'avoir une protection totalement
sûre du texte, mais en prime, on a la garantie d'envoyer du XHTML bien
formé, ce qui est toujours une bonne chose.

Ceci dit, je dis ça dans l'absolu, mais je ne sais pas à quel point ça peut
s'appliquer au cas décrit dans ce thread, à savoir en PHP : ce langage
permet-il de définir des types abstraits facilement ? et est-il possible de
bloquer l'inclusion de code dans la page par un bête print ? J'avoue que je
n'en sais rien.


PS : J'ai dit plus tôt « toujours par GET, les méthodes POST ne sont pas
drôles », je voulais juste dire par là qu'en POST, « <script
type='text/javascript'> » serait codé tout bêtement en « <script
type='text/javascript'> », sans aucun codage particulier de caractères, ce
qui rendait mon propos peu illustratif. Pour une vraie application, dès
qu'il y a des données sensibles, il faut clairement utiliser un POST, je
suis bien d'accord.

Avatar
Nicolas George
John Gallet wrote in message :
Est-ce que je résume ton point de vue en disant ceci :

Il faut rendre inerte du code offensif pouvant potentiellement être
dangereux lors de sa relecture ultérieure par une application. Comme les
trsnformations nécessaire à le rendre inerte dépendent de l'application
(par exemple : transformer en entités html pour un navigateur ou
[INSERER ICI UN AUTRE EXEMPLE DANS UNE AUTRE APPLI] il est préférable de
faire ces traductions quand on récupère les informations par type de
canal de sortie.


Oui, c'est exactement ça.

Auquel j'ajouterai :

Si on a un seul canal de sortie (par exemple, l'interface html de
l'administrateur) on peut aussi faire la transformation avant le
stockage.


Oui, c'est vrai, mais pour que ce soit une bonne idée, il faut :

- être sûr qu'on ne voudra jamais ajouter un nouveau canal de sortie
(serveur NNTP, document PDF, ou que sais-je) ;

- être sûr qu'on ne voudra pas ajouter des traitements (mise en valeur de
certains mots, par exemple) après le stockage.

En fait ce qui me gêne dans le "désamorçage a posteriori" c'est que je
suis quasi certain qu'à un moment ou à un autre, "on"(1) va oublier de
le faire. Alors que si je fais le déminage tout de suite, je suis
peinard.


Oui, c'est vrai, mais on peut tout aussi bien oublier de protéger en entrée
qu'en sortie, le risque est fondamentalement le même. Avec perl, on a le
caractère pourri (tainted) des chaînes qui permet en partie de le détecter,
mais ça reste limité (et je ne sais pas si PHP a un même mécanisme).

Quant au moyen d'éviter au maximum le risque d'oublier de protéger en
sortie, cf. <con0e7$29id$, qui a été modéré en même
temps que le message auquel je réponds : l'utilisation d'un type abstrait
plutôt que la sortie directe de chaînes apporte cette sécurité, en plus de
certains avantages.

Avatar
Dominique Blas
John Gallet wrote:

Re,


Le fait d'avoir un seul script (*) à maintenir, facilite déjà la
maintenance (sic) puis le déverminage et accessoirement la mise en place
de points de contrôle en vue de pister.


Le point d'entrée unique est toujours intéressant, tout à fait d'accord.

Dans ce que j'ai écrit les gains directs sont apportés :
- par la configuration du serveur Web en vue de rediriger
SYSTEMATIQUEMENT sur la page d'accueil : cela interdit toute saisie d'un
URL quelconque (avec des ? et des & en vue d'une injection SQL par
exemple) ; - par le filtrage des entrées.


Oui sans hésiter pour le filtrage des entrées. En revanche je n'arrive
pas à voir exactement pourquoi le point d'entrée unique
Je préfèrerais que l'on parle de configuration du serveur Web fournissant

une redirection systématique sur un contrôleur (le point d'entrée unique)
car parler de point d'entrée unique sans évoquer l'aspect coercitif peut
laisser supposer qu'on peut le contourner.
Note bien que cet aspect coercitif est indépendant de la notion de point
d'entrée : on peut très bien utiliser la coercition sur de multiples pages
de manière à distinguer plusieurs sites Web par exemple qui se partagent
pour des raisons techniques un même répertoire (ils se partagent les mêmes
images et on a interdit les liens) : un internaute arrivant par un site ne
peut ainsi absolument pas voir les pages dépendant d'un autre site.
Simplement dans ce cas (absence de point d'entrée unique) le serveur Web
n'aynat de tout manière pas sconscience de ce qu'est un niveau
d'autorisation on ne porura pas faire le distingo entre une page réservée à
un administrateur et le reste des autres pages ce qu'une application peut
faire, elle.
Enfin, c'est un détail.

te protège
contre une injection SQL, tu peux très bien la recevoir dans une de tes

variables POST. Je sens que je n'ai pas encore compris tous les
principes que tu mets en oeuvre dans ta notion d'automate à ce sujet.
Quand tu dois écrire ta clause WHERE, c'est bien parce que tes données
user sont filtrées que tu es protégé, pas parce qu'elles ont été
transmises en POST.
Oui, dans le cas d'un POST mais s'il s'agit d'un GET et que le code manipule

de manière transparente pour le développeur les GET et les POST (comme
c'est le cas dans PHP) et que le développeur n'a spécifiquement filtré que
les POST ? C'est con je sais mais ça arrive.

En fait les 2 fonctionnent de paire en ce qui concerne les injections mais
la redirection systématique protège également (et c'est le cas le plus
courant le filtrage parant à la majorité des soucis d'injection) contre les
exécutions arbitraires qu'elles soient lièes à l'installation (Apache peut
être fourni avec des scripts de démo que l'on pourrait utiliser pour
parcourir des fichiers, exécuter dans le contexte du serveur Web d'autres
scripts) ou à un niveau de services. Imaginons que seuls les
administrateurs aient accès à un script admin.mon (nous ne sommes donc plus
dans le cs du point d'entrée unique) qu'est ce qui interdit un petit malin
de pointer directement ce script ? Bien entendu il y aura, on peut
l'espérer, un protection liée à l'authentififation mais s'il n'y a en pas
ou que celel-ci n'a pas prévu ce cas ?
En fait la redirection n'est pas une protection en soi : elle ne fiat pas
tout loin de là mais contribue, directement, à l'instar du filtrage des
entrées à la sécurité du développement.

Pour en revenir au point d'entrée unique, si on ne sait pas développer un
site tenant compte de ce type de config autant oublier.



Je sais que ce type de saisie ouverte doit être acceptée parfois (Wiki,
forums). Sans aller jusqu'à recoder un interpréteur je pense qu'une
translation est peut-être une solution.
Je m'explique. Le système de filtrage explicité auparavant intervient dès
la soumission du formulaire en éléminant les caractères inacceptables. On
peut très bien imaginer qu'au lieu de les éliminer dans le cas d'une zone
ouverte il les translate ( ex : ';' devient 0xff, '$' devient 0xfe, etc)
voire les coder sur 2 octets (UTF-8)).


Si je comprends bien, au lieu de les "traduire" en leur équivalent
htmlentity tu les traduis en code interne, en espérant que l'application
qui les récupèrera et les affichera (par exemple IE dans le cas d'un
script JS offensif stocké en base) ne saura pas les comprendre après.
C'est l'idée mais je n'y ai pas réfléchi complètement en fait, n'ayant

jamais à traiter le sujet : dans aucun des sites développés par moi il n'y
avait de zone de texte complètement libre.
Si seulement le fait d'afficher des htmlentities protégeait le navigateur
mais il y a de bonnes chances que celui-ci interprète ces entités comme le
fait IE lorsqu'il croise des codes Unicode : il les interprète de manière
récursive jsuqu'à ce qu'il n'y en ait plus. D'om les immenses facilités de
phishing (ou d'obscurcissement de l'URL réellement pointé) dont on
dispose. Que peut-on faire contre ça ?

L'objectif est d'éviter tout type d'injection y compris le XSS. Or, la
translation, telle que je la présente a forcément lieu en sens inverse
lorsqu'on présente les données stockées et si une balise IFRAME ou Cross
scripting s'y trouve caché elle réapparaîtra et sera éventuellement
interprétée par le navigateur foireux.
A part supprimer les caractères stupides (< > ? /) ou réafficher la zone
saisie sous la forme d'une image (interdisant de ce fait une correction)
je ne vois pas comment faire.

(...)

Pire que ça :
$dad=mysql_connect(....);
if($dad=úLSE)
{
require('std_excuse.html');
exit();
}
Je peux pas me connecter à la base, je sais que sans la base je peux pas
faire le boulot qui m'est demandé, je me casse. Je vais pas générer une
exception à gérer je sais pas où qui sera trappée ou pas par je sais pas
qui, je peux rien faire, j'arrête les frais.
Je sais, je triche, c'est un exemple qui s'y prête bien.
Mouais. DAns un site B2B la moindre des choses c'est d repartir à

l'authentification et de supprimer les autorisations.

Par ailleurs, si tu as 100 possibilités d'erreurs à la connexion dans
l'ensemble de ton code tu auras 100 fois
if($dad=úLSE)
{
require('std_excuse.html');
exit();
}
?
Et si tu veux changer la page d'infos tu te fend d'une ligen de scripts afin
de faire un S&R dans tous les fichiers ?
Soit.
A chacun son deuil. :-)

N'oublies pas que nous parlons
d'applications Web et que s'il y a un pépin sur le SGBD le serveur
d'applications lui est toujours vivant


Pas nécessairement, d'aucuns sont assez "doués" pour aller chercher de
temps à autres un paramètre de config ou truc du genre. Mais quoi qu'il
en soit, je vois pas pourquoi je continuerais mon programme si de toutes
façons je peux pas exécuter mon boulot.
Dans la même veine, je vais pas me priver de faire un ROLLBACK et à me
casser violemment si ma requête échoue : ou la connexion est dans les
choux (genre : équipement réseau un peu sensible qui l'a coupée parce
que temps de session dépassé) ou il y a une erreur de syntaxe (attaque
probable si j'ai bien recetté mon programme).

et qu'il doit donc prendre en charge
cet évènement est en avertir proprement l'internaute ainsi que
l'administrateur (éviter le message trop souvent vu sur les IIS : SQL
server Error 0x80EF000C ...(j'invente) par exemple).


Tout à fait, mais on peut très souvent parfaitement gérer ça directement
en local, dès la détection de l'erreur. Et le code en est *à mon sens*
grandement plus lisible et facile à débugger que de suivre les try/catch
dans tous les coins :
Dans mon exemple les ty catch comme tu dis sont tous situés dans le fameux

contrôleur : tout remonte à ce niveau.
Au moins si j'en ai oublié une je sais où l'ajouter.
C'est consommateur de ressources, je sais mais quelle propreté !


(...)

Je percute vraiment pas en quoi enlever le GET est un apport de sécurité
pour le site (à part, on l'a déjà vu, pour les données sensibles type
password ou l'identifiant de session qui se fait écrire grâce au
referrer dans le log d'un attaquant).
Ben c'est ça, uniquement.

Il y a encore quelques années peu de personne sachant comment transmettre
des données en POST (c'est pourtant assez bête) on considérait le POST
comme plus << secure >> mais c'était du pipeau de toute manière.
Ce qu'on peut faire en GET on le fait de la même manière en POST, donc
autant interdire le GET non ?
Là je me place du point de vue du petit malin.
Du point de vue du développeur le POST l'oblige à gérer d'éventuels champs
cachés dans les formulaires. C'est donc un supplément de boulot ... limité
toutefois car il aura de toute manière à gérer des zones de saisies. Alors
un peu plus ou un peu moins ...
Du point de vue de l'internaute cela lui interdit pratiquement les accès
directs.
Que je reçoive la donnée vérolée
en get ou en post, elle est tout aussi vérolée.
Ben oui.

C'est pas pour faire ma
mauvaise tête, ça m'intéresse vraiment de comprendre.

db


--
email : usenet blas net