Post by Olivier MiakinenMême si cette URL n'est utilisée que pour une requête HEAD, et que la
seule information utile qu'on en retient est un éventuel code d'erreur ?
Oui.
Mais le problème de sécurité n'est pas *que* sur le serveur faisant la
requête, ce dernier peut devenir l'origine d'une attaque de type deni de
service (distribué ou non).
Voici quelques exemples de choses (tirées d'un papier sur la sécurité
d'OpenID que j'avais lu mais je n'ai plus la référence en tête) auxquelles
il faut penser, c'est à dire des URLs ennuyeuses :
- http://www.nsa.gov:1/, http://www.nsa.gov:2/, http://www.nsa.gov:3/
on fait une attaque de déni de service sur tous les ports, ou un
équivalent « nmap » (même si ce n'est pas du HTTP sur tous les ports, le
client risque de retourner un code différent entre « pas de réponse » et
« une réponse qui ne ressemble pas à du HTTP »).
Et, indépendamment d'une réponse ou non du serveur appelé, cela peut être
gênant juste d'initier un trafic vers certaines destinations...
- https://192.168.1.15/internal/auth?ip=1.1.1.1
URL interne à un réseau normalement
- http://localhost:8080/
même genre, et contournement d'un éventuel pare-feu local (sur le
serveur web)
- http://www.youtube.com/largemovie.flv
déni de service en temps et RAM consommée (sauf si on fait un HEAD, ok)
- file:///dev/null
il faut probablement veiller à ne faire que du HTTP/HTTPS
- http://www.example.com/register.pl?user=toto&pass=titi
remplissage « automatique » de formulaires à distance (sauf CAPTCHA et
autres), ou spam de blogs et autres, etc...
Post by Olivier MiakinenJe ne vois pas quel problème de sécurité cela pourrait poser pour
l'appelant : il y a *beaucoup* moins de risques que pour un simple
internaute cliquant sur un lien avec un navigateur qui ferait un GET au
lieu d'un HEAD et qui, en outre, interpréterait le JavaScript.
Il est difficile de quantifier les dangers respectifs de différentes
pratiques.
L'appelant devient la source d'un certain trafic (une requête HTTP), comme
délégataire du client qui a soumis l'URL. Il est alors *responsable* de ce
trafic, comme l'est un proxy HTTP. Il vaut mieux qu'il soit absolument sûr
de la légitimité de ce trafic, car sinon il est lui-même vulnérable (cf
exemple http://localhost:8080/ plus haut) ou devient un intermédiaire
pour exploiter des vulnérabilités ailleurs (ce qui n'est pas beaucoup plus
enviable... avec les lois en préparation c'est un coup à perdre son accès
ADSL :-))
Post by Olivier MiakinenEt même pour l'appelé : s'il n'implémente pas d'effet de bord aux
requêtes HEAD, il ne risque pas grand chose -- et inversement s'il
reformate son disque dur en réponse à un HEAD, c'est bien fait pour sa
pomme !
Pas seulement. Il peut faire confiance implicite au serveur appelant,
parce qu'il est dans le même réseau, etc... et donc permettre l'accès à
une ressource privée. Oui, le modèle de sécurité du serveur appelé est en
partie en défaut. Mais, en pratique, ce genre de défaut est fréquent.
Ou à l'opposé, il peut mettre en place de la QoS et donc refuser l'accès
au serveur appelant, parce qu'il a déjà vu son IP trop souvent dans le
passé, alors que la ressource demandée serait accessible depuis un client
« normal ».
De même, un client HTTP s'identifie (User-Agent & co, mais aussi en-têtes
Accept souvent absent par exemple, ce qui est détecté par des outils
comme mod_security, etc.) différemment d'un navigateur, ce qui peut
entraîner une différence de réponse de la part d'un serveur (ok, le
problème est du ressort du serveur HTTP appelé, mais en pratique on voit
souvent ce genre de situations).
Post by Olivier MiakinenPost by Patrick Mevzekhttp://search.cpan.org/~bradfitz/LWPx-ParanoidAgent-1.03/lib/LWPx/ParanoidAgent.pm
(avec quelques explications sur les pièges évités).
Je n'ai pas bien compris ce que ça fait (il faut dire que je ne suis pas
allé voir ce qu'était LWP::UserAgent dont il dérive).
LWP::UserAgent est un client HTTP en Perl (comme curl donc).
LWPx::ParanoidAgent est une sous-classe du précédent, donc toujours un
client HTTP comme curl mais qui en plus vérifie certaines choses comme :
- suppression de la fonctionnalité proxy (requêtes directes toujours)
- utilisation seulement des "schemes" http et https
- impossible de se connecter sur des IPs privées (192.168.x etc ...)
- prise en compte de listes noires de noms/IPs
- prise en compte d'un time out global pour être sûr de s'arrêter au bout
d'un certain temps et de ne pas rester bloqué à la merci d'un serveur
distant qui répond, intentionnellement ou non, très lentement.
(cas précis évoqué au début de ce fil)
En tenant compte des redirections HTTP (autre point à prendre en compte
globalement, les navigateurs détectent les boucles de redirection et
s'arrêtent après un certain nombre de sauts, je ne sais pas si c'est le
cas, par défaut, dans tous les clients HTTP), des chaînes de CNAME dans les
DNS (qui pourraient provoquer des boucles), etc.
Post by Olivier MiakinenPost by Patrick MevzekEt bien sûr là on ne parle même pas d'éventuelles failles dans le
client HTTP en lui-même juste de failles découlant du fait d'accepter
de l'extérieur une information réutilisée telle quelle.
Oui, bien sûr. Mais dans ce cas, quel genre de contrôle ferais-tu sur
l'URL qui pourrait minimiser les risques lors de la connexion par CURL ?
Par rapport au besoin du posteur initial, il me semble qu'un simple test
syntaxique sur l'URL entrée suffit (ce qui n'est même pas fait dans
l'exemple donné au début, même coller http:// systématiquement au début,
ca me paraît dangereux, moi si on me demande une adresse web, c'est une
URL, donc je mets déjà le http:// au début :-)), il n'y a pas besoin de
curl. Je ne vois pas bien l'intérêt de vérifier si le serveur en question
répond, existe, etc. (sauf si on cherche réellement à récupèrer quelque
chose dessus, genre une image pour un avatar ou autre) parce qu'il
pourrait y avoir *plein* de problèmes, y compris du côté de l'appelant
(problèmes réseau, etc.) qui font que le serveur ne répond pas, et
répondra plus tard, ou le contraire, etc. [ Ca me rappelle ZoneCheck cette
affaire :-)] Ca, plus les risques auxquels on s'expose fait que je ne
trouve pas utile d'aller faire la requête HTTP en question.
Maintenant s'il y en a réellement besoin, il est fort probable qu'il n'y
en a pas besoin de manière synchrone. Je prendrais donc l'URL que me donne
l'utilisateur et (après un éventuel simple test syntaxique), la stocke
quelque part. Après un processus asynchrone indépendant traite toutes les
URLs en attente.
Ainsi :
- il est complétement décorrélé du serveur web, et ne peut pas planter ce
dernier ni permettre un contrôle quelconque, si tant soit peu évidemment
qu'on le sépare correctement (exemple: utilisateur différent, répertoires
séparés, droits précis, etc.)
- si curl ou n'importe quel client est vulnérable, on arrête
temporairement les vérifications, ou on change de client, etc. Tout ceci
sans conséquences pour le service web, et a priori assez simplement parce
qu'on aura qu'une dizaine de lignes de code spécifiques pour cette tâche à
corriger, plutôt qu'une éventuelle non négligeable partie du code (PHP ou
non) du serveur web, dès qu'on n'a pas pensé à mettre ca proprement dans
une fonction/un fichier à part.
Ok, c'est plus « complexe » à mettre en oeuvre, mais ca utilise les bonnes
pratiques de la sécurité (séparation des processus, donner toujours le
minimum de droits pour faire une tâche, etc.)
Cette approche asynchrone ne fonctionne pas pour des besoins comme ceux
d'OpenID, d'où toutes les précautions explorées plus haut, sans que cela
soit exhaustif évidemment.
Tout ce qui précède est évidemment en grande partie complétement
indépendant à la fois du client HTTP en question (curl) et du langage (PHP).
Après il faut ajouter les failles de sécurité propres à ces deux éléments.
Pour curl lui-même par exemple :
http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=curl
(en particulier la plus récente
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-4850
est problématique pour ce qui nous intéresse ici)
Pour PHP et sa sécurité, trop de liens à mentionner :-)
À noter que personnellement, pour moi, allow_url_fopen est mieux à Off,
donc pas de fopen('http://...') comme évoqué au début.
(mélanger les ressources « locales » comme les fichiers sur le disque dur
du serveur et les ressources « distantes » accessibles par HTTP/FTP/etc.
en supprimant toute distinction entre les deux me paraît être une
mauvaise chose, en tout cas du point de vue de la sécurité ; cela illustre
une façon générale de penser aux problèmes de sécurité que je donne en
cours, à savoir que quand une information change de contexte, ou qu'on
perd le contexte, cela créé un risque. Cf toutes les attaques type XSS,
etc.)
--
Patrick Mevzek . . . . . . . . . . . . . . Dot and Co
<http://www.dotandco.net/> <http://www.dotandco.com/>