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
Dominique Blas
John Gallet wrote:

Bonjour,

1. les entrèes doivent être systématiquement traitées afin
d'éliminer les caractères n'ayant rien à y faire ( '&', '$', ';',
',', '!', ':', '', etc) selon la nature de la zone de saisie (
alpha, alphanum, num, flottant,
etc) ; ceci concerne également le contenu du POST (ou du GET) :
les champs
doivent être traités avant usage dans une fonction ou une
méthode.


Est-ce suffisant d'interdire explicitement certains caractères ? Pour le
moment j'ai plutôt tendance à lister les caractères explicitement
autorisés.
La bonne règle en sécurité est : << tout ce qui n'est pas expressément

autorisé est interdit >>.
Toutefois on pourra, pour des raisons de simplicité, regrouper les besoins
en familles : num, alpha, alphanum et texte sans caractère de ponctuation
puis texte avec caractères de ponctuation triés sur le volet (pas de $, de
';', de '?', de '', de '~', de '&', de '^', de '[', de ']', de '(', de
')', de '{', de '}', de '|', de ''', de '`', de '!', de ':', de '*', de '"'
dans la mesure du possible.

via http://www.toto.com quelle que soit la vue réellement
affichée derrière. Ainsi que l'on tente
www.toto.com/tartampion.html ou
www.toto.com?TEST=toto&SQLREQUEST=SELECT+ ... cela n'a aucune
influence étant donné que seul l'état de l'utilisateur (conservé
dans un objet Session) indique quelle est la vue réellement
utilisée ;


Je n'ai pas compris comment mettre ça en oeuvre. Tu stockes dans ta
session les différents choix possibles de l'utilisateur par rapport à la
dernière page générée ?
Tu as une page avec 3 liens http, un FORM pour
une recherche et un FORM de saisie de tes coordonnées, comment peux-tu
prévoir à l'avance quelle "vue" il va falloir afficher ? Une sorte
d'arbre de décision ?
Non. A l'utilisateur est associé un état et c'est cet état qui détermine la

vue affichée. Vois Struts qui est le framework le plus connu pour implanter
le modèle MVC2 (*).
Bon Struts est un peu compliqué avec tous ses fichiers à configurer c'est
vrai.
Je m'en vas te l'expliquer autrement mon gars.
Tu imagines un source (PHP, Java, C# ou Python, Unix shell, ce que tu veux)
unique. Ce source débute par une initialisation de variables, patati,
patata et le démarrage de la session (pour les langages qui sont contraints
d'en passer par là). Si la session est déjà démarré on récupère les
variables sérialisées sinon on les initialise.
A la fin du script on sérialise les variables initialisées précédemment ou
désérialisées. Ces variables contiennent des informations qui doivent être
conservées de vue en vue, OK ?
Et entre les 2 qu'avons-nous ?
Et bien nous avons un gros SWITCH sur la valeur de la variable qui contient
l'identifiant de la vue à afficher ainsi qu'un bout de code qui va chercher
le source de la vue à afficher, le traite éventuellement (transformation
XSLT) et le balance dans le tuyau.

Cet identifiant est initialisé, lors de l'arrivée de l'utilisateur, à la
première vue à afficher (on va dire '1' pour simplifier).
La première fois nous passons donc dans le SWITCH avec la valeur '1'. Dans
cette rubrique la variable POST est vide (auucne soumission n'a encore eu
lieu) : on positionne évenutellement quelques variables dont la valeur sera
reprise dans la vue et on affiche la vue n° 1.
Là l'utilisateur va saisir quelques trucs et soumettre la vue.
On repasse donc dans le SWITCH avec toujours la valeur '1' mais cette
fois-ci, étant donné que nous avons des champs POST positionnés (et
filtrés), nous entrons dans un SOUS-SWITCH qui va, outre peut-être formuler
des requêtes (authentification par exemple) et positionner d'autres
variables, prendre une décision : quelle est la prochaine vue à atribuer à
notre utilisateur ?

Imaginons que la vue n° 1 soit le panneau d'identification/authentification.
Si notre utilisatuer s'est correctement authentifié alors le pointeur de vue
passe à la valuer '2' (par exemple) sinon il reste à la valeur '1' et dans
ce cas l'utilisateur se retrouve avec la même vue que précédemment avec,
accessoirement, un petit message d'insulte.
Etc, etc.
En clair nous avons un seul source (+ 1 par fenêtre fille) qui se contente
de sélectionner d'autres fichiers (les vues), de les traiter (il y a sans
doute des boucles [pour afficher les select par exemple], des conditions,
etc) et de les balancer au serveur Web.
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.

3. A l'arrière j'ajoute que les erreurs doivent être traitées
correctement
Oui, sans aucun doute.


(le modèle des exceptions est utile pour cela)
Mouais, enfin là on rentre dans de l'implémentation et en ce qui me

concerne, je ne cite qu'un seul exemple de gestion des exceptions : le
premier vol d'ariane V (boum). Je dirais qu'au contraire, au lieu de
laisser le code appelant se démm....er avec l'exception, plus vite on la
gère, mieux ça vaut, mais on va arriver vite à la querelle de clochers.
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.
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. Le système des exceptions est lent à
l'exécution (il faut dépiler) mais conduit à un code bien plus simple mais
peut-être pas compréhensible par tous. Après cela dépend sur quoi on fait
porter la priorité : le source ou l'exécution.
M'enfin ce n'est pas le sujet.

Bien entendu, un modèle objet bien fait est très utile à l'implantation
de ces quelques règles en permettant le découpage des différentes étapes.


Bien entendu, on peut parfaitement s'en passer et faire plus simple.
Mais là encore, il s'agit de querelles de clocher.
Pas forcément. S'il s'agit de développer 3 pages qui se font la gueule

inutile de partir dans le l'objet mais c'est comme partout (quel que soit
le langage) une fois que l'on dispose de squelettes ça va plus vite même
pour faire 3 pages.

db

(*) Modèle Vue Contrôleur.
Le script unique évoqué est le contrôleur qui assure la sélection, le
traitement et l'affichage des vues ainsi que l'utilisation du modèle
(modèle de données) au travers d'appels vers des fonctions spécialisées
(des objets spécialisés [ex des EJB]) qui implantent le modèle justement.


--
email : usenet blas net


Avatar
Dominique Blas
Eric Razny wrote:


Je pense que c'est l'idée, mais cela a des conséquences facheuses: un
même utilisateur ne peut être connecté deux fois avec la même session (et
parfois, par exemple avec des cookies, il ne peut pas se connecter deux
fois avec deux sessions différentes) dans deux fenêtres différentes, sans
perturbations. Pour ceux qui usent des onglets, c'est très pénalisant,
c'est pour ca que personnellement je ne recommande pas trop cette piste.
De quoi parlons-nous là ?


De sécurité ou de plaisir de l'utilisateur ?
Pourquoi autoriserait-on un utilisateur à se connecter 2 fois (avec le même
identifiant s'entend) ? Là je ne comprends pas bien.
Lorsque vous êtes sur un site marchand vous pouvez être connecté 2 fois à la
banque ?

Ca me rappelle la discussion qui traîne depuis des mois sur un autre ng au
sujet de l'antispam. D'un côté tout le monde en a marre mais lorsqu'on
propose des solutions, certes contraignantes mais efficaces, les mêmes
personnes râlent qu'ils ne vont plus pouvoir expédier du mail de chez eux
en utilisant le domaine de courriers de l'entreprise, qu'ils n'apprécient
pas que leur entreprise ne pourra plus relayer leur courrier à destination
de leur BAL d'entreprise vers une de leurs BAL privées, etc, etc.
C'est toujours la même chose : on veut de la sécurité mais pas de
contraintes.
Choisissez. J'ai exposé une méthode, libre à ceux qui l'ont lu de l'utiliser
ou non.
Je confirme, je connaissais quelques sites comme ça c'est chiant au
possible.
En quoi est-ce chiant ?


En plus je suis loin d'être sur que c'est positif pour la sécurité.
Certe du côté du site un utilisateur est dans un état (au sens automate)
bien défini, mais un utilisateur peut croire qu'il est en sécurité après
un surf alors qu'en relancant une page bookmarkée on se retrouve
connecté ;
FAUX. Si l'utilisateur a éteint son navigateur ou a poireauté jusqu'à

l'expiration de session (disons 20 minutes) il a beau avoir bookmarqué ce
qu'il veut il revient au panneau d'authentification (son état est '1' pour
reprendre mon exemple).
S'il rafraîchit l'affichage avant l'expiration de session il reste là où il
était quel que soit le nombre de rafraîchissements. En outre on lui
interdit d'utiliser la touche précédent.

sur une autre page certes, mais "connecté" (au sens avec
certaines actions qui ne doivent pas être possible sans login/passwd au
minimum). Même un cookie de session est plus sur :-/ (en plus la gestion
du bouzingue doit vite devenir... amusante quand le site évolue.)
Simplissime :

ajout d'un nouveau 'case of' par nouvelle vue ;
écriture de la ou des nouvelles vues (il faut bien en passer par là) ;
modification éventuelle des spécificateurs d'état dans les autres 'case
of' pour le cas où l'arbre de navigation décisionnelle aurait subi des
modifications.
Pour ma part cet arbre est dans un fichier XML et est donc extérieur au
code.


Sinon aussi, à propos de sécurité en général : faire simple[1] est
souvent gage de maintenance aisée et la sécu du bidule peut être plus
facilement assurée.

Cela pose un problème aussi si on souhaite laisser la possibilité aux
utilisateurs de mettre en bookmark n'importe quelle page du site.
D'où l'intérêt de ne JAMAIS afficher d'autre URL que celui de l'accueil.



db
--
email : usenet blas net


Avatar
Dominique Blas
John Gallet wrote:

(re) bonjour,

Et merci aux contributeurs du thread. Je vais essayer de faire une
synthèse des risques et de leur contre-mesure, pour le moment, j'ai
l'impression que se dégagent deux types d'acteurs :

- le développeur
- le responsable du déploiement de l'application.
Hum, pourquoi ne pas parler simplement du chef de projet.

Après tout c'est lui qui << dessine >> l'application et en assure la
recette.
Le développeur ne fait que suivre ses consignes quel que soit le langage.
Au-delà du chef de projet nous avons l'administrateur système, charge à lui
de s'assurer effectivement que le serveur Web est correctement installé
que les permissions et ACL sont correctement réglées sur le système de
fichier et que les filtres réseau (sur la machine elle-même) sont
correctement paramétrés selon les préconisations du chef de projet.
Il faut enfin songer à la configuration du serveur qui par elle-même peut
circonvenir à certaines faiblesses de configuration du système de fichiers
(oubli). Qui la prend en charge ? L'administrateur système ou le
webmestre ?
On pourrait également parler de l'administrateur des réseaux mais il n'est
pas directement concerné par l'application.
db
--
email : usenet blas net

Avatar
John Gallet
(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é.

La bonne règle en sécurité est : << tout ce qui n'est pas expressément
autorisé est interdit >>.


On est bien d'accord.

Toutefois on pourra, pour des raisons de simplicité, regrouper les besoins
en familles : num, alpha, alphanum et texte sans caractère de ponctuation
puis texte avec caractères de ponctuation triés sur le volet (pas de $, de
';', de '?', de '', de '~', de '&', de '^', de '[', de ']', de '(', de
')', de '{', de '}', de '|', de ''', de '`', de '!', de ':', de '*', de '"'
dans la mesure du possible.


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.
Donc quid ? Est-ce que ça suffit de les transformer en leur équivalent
html (quitte donc à doubler la taille de tous les champs SGBD pour faire
face à l'embonpoint ainsi ajouté) ? Quand on a un truc du genre <SCRIPT
"caché" en %u0003CSCRIPT si on transforme ça en &pct;u003SCRIPT ça
devrait suffire mais est-ce que j'en oublierais pas des tonnes (si,
sûrement) ? Et quand bien même ça suffirait, si l'application exige que
je stocke aussi des données de mise en forme (gras italique souligné par
exemple) je me démmerde comment ?

Là l'utilisateur va saisir quelques trucs et soumettre la vue.
On repasse donc dans le SWITCH avec toujours la valeur '1' mais cette
fois-ci, étant donné que nous avons des champs POST positionnés (et
filtrés), nous entrons dans un SOUS-SWITCH qui va, outre peut-être formuler
des requêtes (authentification par exemple) et positionner d'autres
variables, prendre une décision : quelle est la prochaine vue à atribuer à
notre utilisateur ?


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

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 ?

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.

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.

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.

a++
JG

Avatar
Patrick Mevzek
Je pense que c'est l'idée, mais cela a des conséquences facheuses: un
même utilisateur ne peut être connecté deux fois avec la même session
(et parfois, par exemple avec des cookies, il ne peut pas se connecter
deux fois avec deux sessions différentes) dans deux fenêtres
différentes, sans perturbations. Pour ceux qui usent des onglets,
c'est très pénalisant, c'est pour ca que personnellement je ne
recommande pas trop cette piste.
De quoi parlons-nous là ?


De sécurité ou de plaisir de l'utilisateur ? Pourquoi autoriserait-on un
utilisateur à se connecter 2 fois (avec le même identifiant s'entend) ?


Une application qui ne me permet pas de naviguer simultanément dans deux
onglets, est pour moi une application ratée car elle me force à
travailler comme elle veut et pas comme je veux.
Si comme si on ne pouvait ouvrir un livre qu'à une seule page
simultanément...

Je confirme, je connaissais quelques sites comme ça c'est chiant au
possible.
En quoi est-ce chiant ?



On n'est pas libre de naviguer comme on veut.
Si la recherche est mal foutue, par exemple, on peut vouloir voire le
détails d'une fiche dans une fenêtre/un onglet à part, tout en gardant le
résultat de la recherche.
Et je parle pas de la gestion du BACK, pas toujours optimale...

sur une autre page certes, mais "connecté" (au sens avec certaines
actions qui ne doivent pas être possible sans login/passwd au minimum).
Même un cookie de session est plus sur :-/ (en plus la gestion du
bouzingue doit vite devenir... amusante quand le site évolue.)
Simplissime :



Il n'y a rien de simple quand on pense à la sécurité...

Sinon aussi, à propos de sécurité en général : faire simple[1] est
souvent gage de maintenance aisée et la sécu du bidule peut être plus
facilement assurée.

Cela pose un problème aussi si on souhaite laisser la possibilité aux
utilisateurs de mettre en bookmark n'importe quelle page du site.
D'où l'intérêt de ne JAMAIS afficher d'autre URL que celui de l'accueil.




Justement non: on peut très bien laisser aux utilisateurs la possibilité
de bookmarker une page bien précise. C'est similaire à ce que je disais
plus haut: ca laisse le contrôle à l'utilisateur et non à votre
application.

--
Patrick Mevzek . . . . . . Dot and Co (Paris, France)
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>



Avatar
Eric Razny

De quoi parlons-nous là ?
De sécurité ou de plaisir de l'utilisateur ?


A moins que tu ne travaille que pour une catégorie de gens forcés à
utiliser ton site, la commodité d'emploi est importante.[1]

Entre deux sites qui proposent le même service, avec une sécurité
équivalente mais un chiant à naviguer l'autre "cool", lequel va rester
(à marketing identique :) )?

Que je sache l'informatique est faite pour faciliter le travail de
l'utilisateur, pas pour le faire se plier à des contraintes *qui n'ont
pas lieu d'être*.

Certes la sécurité et la facilité d'utilisation sont souvent opposé (et
dans ce cas c'est au décideur de faire un compromis). Par contre il
n'est pas plus difficile de faire un site agréable à naviguer en restant
sur(en gardant quelques contraintes comme le temps total online ou le
temps d'inactivité avec une autentification et un suivi qui tient la
route), qu'en pondant une grosse merde qui autosatisfait le programmeur
mais qui fait chier tous les utilisateurs. Et pour avoir vécu certains
de ces sites je peux te dire que certains s'en tienne une couche.


Pourquoi autoriserait-on un utilisateur à se connecter 2 fois (avec le même
identifiant s'entend) ? Là je ne comprends pas bien.


Parce que je veux surfer sur deux fonctionnalités pas dispo en même
temps dans une connexion unique.

Lorsque vous êtes sur un site marchand vous pouvez être connecté 2 fois à la
banque ?


C'est une habitude de faire des parallèles sans rapport et de présenter
ça comme une évidence. Accessoirement si je surfe sur deux instances du
site marchand (qui le gère) et que je passe deux commandes la réponse
est oui.

Autre rappel : il exite encore des coins de notre beau pays où l'adsl
n'est pas parvenu. Le fait de lire une page pendant que d'autres se
chargent fait gagner un temps précieux. Merci de sortir de la tour
d'ivoire. (et même en adsl avec les merdes flashesques qu'on voir
partout les instances multiples ont de beaux jours devant elles[2]).

Ca me rappelle la discussion qui traîne depuis des mois sur un autre ng au
sujet de l'antispam. D'un côté tout le monde en a marre mais lorsqu'on
propose des solutions, certes contraignantes mais efficaces, les mêmes
personnes râlent qu'ils ne vont plus pouvoir expédier du mail de chez eux
en utilisant le domaine de courriers de l'entreprise, qu'ils n'apprécient
pas que leur entreprise ne pourra plus relayer leur courrier à destination
de leur BAL d'entreprise vers une de leurs BAL privées, etc, etc.


Et hop, encore un parallelisme à deux balles, oops cents :).
Accessoirement une certaine honnêteté intellectuelle me fait préciser
que si tu pense à spf le sujet est toujours très polémique (et je ne
parle pas des extrémistes des deux camps) et n'est certainement pas une
solution universelle (genre pas de connexion vers le tcp/25 ou 995 à
partir d'un FAI étranger, et donc macache le relay via sendmail+sasl &
co par exemple). A moins de vouloir transformer le commercial en féru de
technique : on vous à ouvert le port machin à la place... Comme si le
gars comprenait -ou avait à comprendre- de quoi il s'agit.
A noter qu'il est surprenant que tu parle d'antispam... je dois me
tromper tu ne parle donc pas de spf :-/


C'est toujours la même chose : on veut de la sécurité mais pas de
contraintes.


La sécurité peut imposer certaines contraintes. Il est complètement
différent d'en ajouter quand elles n'ont pas lieu d'être.

Choisissez. J'ai exposé une méthode, libre à ceux qui l'ont lu de l'utiliser
ou non.


Permets moi de donner mon avis et d'exprimer en quoi j'y vois un problème.

Je confirme, je connaissais quelques sites comme ça c'est chiant au
possible.


En quoi est-ce chiant ?



Voir au dessus.


En plus je suis loin d'être sur que c'est positif pour la sécurité.
Certe du côté du site un utilisateur est dans un état (au sens automate)
bien défini, mais un utilisateur peut croire qu'il est en sécurité après
un surf alors qu'en relancant une page bookmarkée on se retrouve
connecté ;


FAUX. Si l'utilisateur a éteint son navigateur ou a poireauté jusqu'à
l'expiration de session (disons 20 minutes) il a beau avoir bookmarqué ce
qu'il veut il revient au panneau d'authentification (son état est '1' pour
reprendre mon exemple).


Quelques utilisateurs confondent "fermer une page" et, pour reprendre
ton expression "éteindre son navigateur". Le vrai problème est de toute
façon de faire comprendre comment quitter proprement sa session pour
éviter les risques. Sur certains sites j'ai eu des problème pour trouver
où je dois me deconnecter -et je sais que je dois le faire-. Et j'ai la
simple impression de ne pas être tout à fait M. Lambda au niveau
utilisateur. Alors quid du "commun" (au sens noble du terme)?


sur une autre page certes, mais "connecté" (au sens avec
certaines actions qui ne doivent pas être possible sans login/passwd au
minimum). Même un cookie de session est plus sur :-/ (en plus la gestion
du bouzingue doit vite devenir... amusante quand le site évolue.)


Simplissime :
ajout d'un nouveau 'case of' par nouvelle vue ;
écriture de la ou des nouvelles vues (il faut bien en passer par là) ;
modification éventuelle des spécificateurs d'état dans les autres 'case
of' pour le cas où l'arbre de navigation décisionnelle aurait subi des
modifications.
Pour ma part cet arbre est dans un fichier XML et est donc extérieur au
code.


J'adore le simplissime en sécurité quand il s'agit de coder. Dans le cas
d'un site complexe ta "nouvelle vue" ne va pas se limiter à une simple
page à afficher mais à une série d'interaction avec le reste de
l'application. Même dans les modèles objets (langage objet et conception
objet) il faut se méfier -surtout dès la conception initiale- des effets
de bord (le composant réagit bien comme prévu, mais ce qui n'a pas été
prévu c'est l'état global de l'état des composants et c'est donc le
resultat final qui pose problème.) La modularité c'est à la conception.

Sinon aussi, à propos de sécurité en général : faire simple[1] est
souvent gage de maintenance aisée et la sécu du bidule peut être plus
facilement assurée.


Cela pose un problème aussi si on souhaite laisser la possibilité aux
utilisateurs de mettre en bookmark n'importe quelle page du site.



D'où l'intérêt de ne JAMAIS afficher d'autre URL que celui de l'accueil.


Au contraire, je préfère et de loin savoir ou je suis sur l'url (ce en
quoi les frames me font chier, j'ai le click droit qui fatigue :) ) et
surtout sur le bookmark (même si je peux nommer plus explicitement
celui-ci). Et préfère de loin (RTC oblige ici quand je ne suis pas au
bureau sur la LS) pouvoir accéder directement à une partie d'un site
-url complète donc- (qui va intercepter le requête et redemander un
login par exemple) qu'à me coltiner une succession de pages qui n'est
idéale que dans le cerveau à l'égo surdéveloppé du concepteur du site.

Eric

[1] Pour moi elle est importante quel que soit le type de gus en face.
[2] Bon, ok, je suis un peu de mauvaise fois : elle sont filtrée les
merdes flashiques la plupart du temps :)

--
L'invulnérable :
Je ne pense pas etre piratable, infectable par un trojen oui!
Vu sur fcs un jour de mars 2004.



Avatar
Eric Razny

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

Il faut certe une sécurité dans le codage (objet principal de ton post,
enrichi depuis avec les remarques sur la mise en place des services, la
sécu du réseau etc) mais aussi penser que l'appli peut évoluer. Et que
l'ajout d'une fonctionnalité peut se montrer urgent (pas bon pour la
sécu ça :) ). Si en plus du risque du à l'urgence tu ajoute des
possibilités d'effet de bord à droite et à gauche c'est la cata.

Donc, oui, la simplicité (dans le bon sens du terme) est un plus
sécuritaire.

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

[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 :)

Eric

[1] Cogitation qui permet ensuite à l'appli de rester simple, justement :)

--
L'invulnérable :
Je ne pense pas etre piratable, infectable par un trojen oui!
Vu sur fcs un jour de mars 2004.


Avatar
Nicolas George
John Gallet wrote in message :
Donc quid ? Est-ce que ça suffit de les transformer en leur équivalent
html (quitte donc à doubler la taille de tous les champs SGBD pour faire
face à l'embonpoint ainsi ajouté) ? Quand on a un truc du genre <SCRIPT
"caché" en %u0003CSCRIPT si on transforme ça en &pct;u003SCRIPT ça
devrait suffire mais est-ce que j'en oublierais pas des tonnes (si,
sûrement) ? Et quand bien même ça suffirait, si l'application exige que
je stocke aussi des données de mise en forme (gras italique souligné par
exemple) je me démmerde comment ?


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.

Lors du passage par un protocole, ces chaînes inertes sont encodées d'une
manière ou d'une autre à la source, et normalement décodées à la cible.
Ainsi, si je veux écrire « <script> » dans une page web, j'encoderai
« &lt;script&gt; », et le navigateur décodera ça en « <script> », chaîne
inerte transmise au moteur de rendu. Réciproquement, un formulaire transmis
par un GET sera encodé avec des %XY, mais le moteur applicatif (bibliothèque
CGI ou autre) à l'arrivée est censé décoder immédiatement ça. Il n'y a
_aucun_ risque de sécurité là : c'est inerte.

Il y une erreur fréquente qui consiste à oublier un changement de contexte,
ou se dire qu'on peut le faire plus tard. Ça conduit à des /%2e%2e/ dans les
URL que le serveur laisser passer. Mais c'est une erreur de programmation,
j'ose dire : de débutant. Si l'on s'en tient à la règle : on ne travaille
qu'avec des données complètement décodées, il n'y a pas de problème.

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.

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.

Et quand bien même ça suffirait, si l'application exige que
je stocke aussi des données de mise en forme (gras italique souligné par
exemple) je me démmerde comment ?


Il faut rajouter une couche au niveau applicatif : la chaîne qu'on a reçu du
moteur HTTP ou CGI comme inerte, était en fait l'encapsulation d'encore un
protocole destiné à encoder du texte enrichi dans du texte brut, décidé plus
ou moins arbitrairement. Ainsi, par exemple, on recevra (toujours par GET,
les méthodes POST ne sont pas drôles) « le+%5Cemph%7bfoobar%7d+bleut%c3%a9 »,
qui sera immédiatement décodé en « le emph{foobar} bleuté », et passé à la
couche applicative au dessus, qui y comprendra « « le » en normal,
« foobar » en italique, « bleuté » en normal », sous forme d'une structure
de données un peu complexe, qu'elle se débrouillera pour sérialiser si elle
doit la stocker sur disque ou dans une base de données. Et si ça doit être
ensuite affiché dans un navigateur web, ce sera envoyé comme « le
<em>foobar</em> bleuté ».

Bien sûr, on peut préférer la notation HTML à la notation LaTeX, et attendre
de l'utilisateur qu'il tape dans son formulaire « le <em>foobar</em>
bleuté » (et qu'on reçoive la même chose après décodage de la requête). 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. Dans
ce cas, si on rencontre une balise <script> inconnue, on réagit à ça comme à
une erreur au moment de la décomposition, exactement comme si on était tombé
sur une commande script inconnue dans la syntaxe pseudo-LaTeX (on peut
traiter cette erreur de diverses manières, dont principalement : envoyer
promener le client, ou bien transformer ça en chaîne inerte et essayer de
continuer).

Évidemment, quand on a comme ça un sous-ensemble du HTML, on a facilement
envie de se dispenser de la phase de décomposition-recomposition, et on
_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.

Avatar
Eric Razny
Ainsi, par exemple, on recevra (toujours par GET,
les méthodes POST ne sont pas drôles)


Ahem, et l'url qui indique en clair le joli password qu'on vient de
saisir sous forme d'étoiles dans le formulaire, en protégeant le clavier
des regards indiscrets, pour envoyer ça en https? Comment ça personne ne
regarde l'écran par dessus mon épaule? :)
[1]

Du coup John ça te fait un élément en plus pour la programmation :
préférer POST à GET quand un élément sensible à l'affichage est saisi ou
qu'on ne doit pas afficher la requète ; il faut donc récupérer la
requète sur l'entrée standard (en faisant gaffe au débordements bien sur
:) ).

Eric

[1] reste le javascript qui va mouliner le formulaire avant de poster,
mais le GET reste dans l'historique et je doute que le script fasse du
chiffrage par clef publique....

--
L'invulnérable :
Je ne pense pas etre piratable, infectable par un trojen oui!
Vu sur fcs un jour de mars 2004.

Avatar
FAb
Eric Razny writes:

Ainsi, par exemple, on recevra (toujours par GET,
les méthodes POST ne sont pas drôles)


Ahem, et l'url qui indique en clair le joli password qu'on vient de saisir sous
forme d'étoiles dans le formulaire, en protégeant le clavier des regards
indiscrets, pour envoyer ça en https? Comment ça personne ne regarde l'écran par
dessus mon épaule? :)
[1]


Vous voulez rire, mais Oreka fait ça (novembre 2004) !! C'est hallucinant pour
un FAI à qui on a envoyé son numéro de compte bancaire... bonjour la confiance.

FAb