La securisation
d'un site web.
L'un des
aspects essentiels d'un site web est
tres souvent négligé : la sécurité.
C'est
pourtant courir le risque de perdre l'intégralite
de ses fichiers du jour au lendemain, de voir
le contenu de son site vandalisé ou de
transmettre des données confidentielles à des
tiers.
Plus communément, votre site peut etre
rendu totalement inaccessible,
independamment de la volonte de votre
hebergeur.
D'où proviennent les failles?
Le niveau
de sécurité d'un service dépend de
nombreux facteurs :infrastructure physique,
équipement réseau, architecture du réseau,
moyens de sauvegarde, redondance, supervision,
systèmes d'exploitation, logiciels employés,
configuration, droits d'accès, scripts... Autant de
points qui méritent chacun la plus grande attention,
car la moindre brisure dans cette chaîne est en
mesure de provoquer son ébranlement.
Les premiers
éléments sont bien entendu laissés aux bons
soins de votre hébergeur, vous devez juste vous assurer
que les mesures minimales ont bien été prises. En
revanche, tout le contenu des sites webs est sous l'entière
responsabilité de leur propriétaire.
La protection
du contenu par authentification.
Certaines
pages de votre site web ne doivent peut-etre pas etre librement
consultables. Cela inclut par
exemple les scripts d'administration et les zones
membres. A
cet effet, les serveurs web ont la faculté
de pouvoir protéger des répertoires par mot de passe.
Les pages s'y trouvant sont alors inaccessiblessi le
client ne fournit pas de preuve d'authentification.
Cette
preuve est consitutuée de trois éléments
:
- Un nom de "zone", caractéristique d'un ensemble
de pages. Un utilisateur authentifié auprès d'une
zone est en mesure de consulter toutes les pages
se trouvant dans la meme zone sans entrer de nouveau les données
d'authentification.
- Un nom d'utilisateur.
- Un mot de passe.
Les serveurs
proposent souvent de nombreuses
méthodes pour stocker lesinformations nécessaires
à l'authentification, les plus courantes étant les
annuaires LDAP, les bases SQL, et les serveurs
Radius. Ces méthodes sont en effet indispensables
aux hébergeurs pour centraliser les comptes
clientset les exploiter via différents services :
web, ftp, dns, mail, facturation... Cependant, l'accès
à un annuaire LDAP ou à l'authentification via un
serveurSQL est rarement proposé sur des serveurs
d'hébergement mutualisé.
Tout espoir
n'est pas perdu puisqu'il est généralement possible
de stocker les données nécessaires à l'authentification
web dans de simples fichiers texte. Inutile d'espérer y
enregistrer des millions de comptes en conservant des performances
correctes, mais lorsqu'il s'agit simplement deréserver
certaines zones à quelques utilisateurs prédéfinis,
cette méthode s'avère très largement suffisante.
La protection
de zones par mot de passe peut
s'effectuer exactement de la meme manière sous
Zeus, Caudium, Roxen et Apache. Il y a donc fort à
parierque ce que nous allons décrire ici fonctionne
sans soucis chez la plupart deshébergeurs et
serveurs dédiés. Bien
qu'il soit tout à fait
envisageable de protéger individuellement des fichiers
explicitement listés, il est généralement
plus
commode de travailler sur des répertoires entiers.
La convention
employée par les serveurs web
précédemment cités consiste à vérifier
la présence
d'un fichier nommé '.htaccess' dans tous les
répertoires menant à une page destinée à
etre
envoyée. Le cas échéant, le contenu du fichier
est
analysé de façon à modifier temporairement
la
configuration du serveur web avant d'envoyer la page
concernée.
Il est important
de bien comprendre que chaque
répertoire menant au fichier contenant la page est lu,
de façon à prendre en considération tous
les
fichiers '.htaccess' éventuels.
Prenons l'exemple
d'une vidéo située dans
/www/html/videos/free/42.mpeg ., avec des fichiers
'.htaccess' comme suit :
- /www/.htaccess
: le fichier contient des directives
qui indiquent au serveurque *toutes* les pages sont
interdites. On peut donc placer des fichiers privés
dans ce répertoire (bases de données, essais divers,
bibliothèques...) .
- /www/html/.htaccess
: le fichier indique que toutes
les pages dont l'extension est '.html' ou '.php' sont
autorisées. Tous les autres fichiers dépendent du
fichier .htaccess du repertoire précédent, et sont
donc interdits. En autorisant explicitement certaines
extensions, on évite l'accès aux fichiers laissés
par
inadvertance dans ce repertoire, aux copies de
sauvegardes, etc.
- /www/html/videos/.htaccess
: le fichier n'autorise
l'envoi qu'après authentification (zone membre) .
- /www/html/videos/free/.htaccess
: le fichier autorise
l'envoi sans authentification de tous les fichiers dont
l'extension est '.mpeg' ou '.mpg'.
Les droits
d'accès au fichier 42.mpeg sont déterminés
d'après l'ensemble des fichiers .htaccess qui y mènent,
évalués dans l'ordre. Un fichier'.htaccess' situé
dans un sous-repertoire peut comporter des directives qui annulent
totalement celles déclarées dans le répertoire
parent. C'est ici le cas : /www/html/videos/ nécessite
une authentification, mais /www/html/videos/free/ s'en dispense
(zone "visiteurs" avec des exemples). En
l'absence d'un fichier '.htaccess', on hérite des propriétés
du répertoire parent, ou, à défaut, de la
configuration d'origine du serveur web. Si l'on retire le fichier
/www/html/videos/free/.htaccess, les fichiers contenus dans ce
répertoire deviennent uniquement accessibles après
authentification. Il
est temps de s'attarder sur le contenu proprement dit de ces fameux
fichiers '.htaccess' . Celui-ci peut etre très complexe
et composé d'un assemblage de nombreuses directives. Nous
allons ici nous restreindre à ceux de l'exemple ci-dessus,
qui sont sans doutes les plus courants.
Comment interdire l'accès à un répertoire.
Voici le contenu
d'un fichier '.htaccess' empechant la lecture de tout ce qui se
trouve dans le répertoire courant (et de ses sous-repértoires,
faute de règle inverse dans d'autres fichiers '.htaccess')
: Order
deny,allowDeny from allSatisfy all Ces
lignes devraient constituer les règles par défaut
de tout site web correctement configuré : tout est interdit,
et l'on autorise ensuite explicitement le contenu.
Comment autoriser certains fichiers.
Un exemple
étant souvent plus parlant qu'une
myriade de détails techniques, voici comment
autoriser tous les fichiers '.avi':
<Files ~ "\.avi$">
Order allow,deny
Allow from all
Satisfy all
</Files>
Etendons cet
exemple aux fichiers '.avi', '.mpg'
et '.mpeg' :
<Files
~ "\.(avi|mpg|mpeg)$">
Order
allow,deny Allow from all Satisfy all </Files>
Notez que
le '.' composant un nom de fichier doit obligatoirement etre précédé
d'un backslash (faute de quoi il fait office de joker et non de
caractère défini) . De meme, la fin du nom du fichier
est symbolisée par le symbole '$' . Si celui-ci doit figurer
dans le nom d'un fichier, il faut là aussi le faire précéder
d'un backslash. Le caractère '|' sépare les différentes
propositions d'une liste, ici délimitée par des
parenthèses.
Comment
restreindre l'accès aux utilisateurs authentifiés.
Les choses
vont maintenant se compliquer légèrement. Nous avons
vu qu'un ensemble de pages protégées faisait intervenir
deux facteurs :
- Un
nom de zone : toutes les pages faisant partie de
la meme zone fonctionneront avec les memes données d'authentification.
Concrètement, le client ne verra la boite de dialogue d'authentification
s'afficher qu'une fois par zone et par session.
- Un couple
nom d'utilisateur/mot de passe. Par
commodité, l'ensemble de ces couples va etre placé
dans un fichier distinct du fichier '.htaccess'.
<Files ~ "^\.ht">
Order deny,allow
Deny from all
Satisfy all
</Files>
AuthType BasicAuthName
"Zone membre"
AuthUserFile "www/html/videos/.htpasswd"
Require valid-user
Satisfy all
Le premier groupe de directives (<Files>...</Files>)
interdit l'accès à tous les fichiers dont le nom
débute par '.ht' . En l'occurrence, ce sont'.htaccess'
et '.htpasswd' qui sont visés, afin d'éviter qu'un
visiteur ne puisse consulter la liste des comptes existants. Ces
règles font souvent partie des règles par défaut
du serveur web, mais les ajouter explicitement dans un fichier
.htaccess évite toute mauvaise surprise.
Nous indiquons ensuite au serveur que l'accès aux
fichiers nécessite d'etre authentifié, n'importe
quel
utilisateur étant accepté ("Require valid-user").
La
zone se nomme "Zone membre" et le fichier contenant
les paires nomd'utilisateur/mot de passe se nomme ".htpasswd"
(nom souvent choisi par convention) . Le chemin www/html/videos/.htpasswd"
est relatif à la racine des fichiers composant les pages
web. Dans quasiment tous les cas, il s'agit simplement du répertoire
où se trouve le fichier, tel qu'il est visible dans un
client FTP. Attention à ne pas précéder le
chemin d'un slash pour que cela soit vrai : 'www/html/videos/.htpasswd'
correspond bien à cette définition, tandis que '/www/html/videos/.htpasswd'
est un chemin absolu, qui nécessite des informations à
obtenir auprès de votre hébergeur (pour un résultat
identique) .
"AuthType
Basic" indique le protocole d'authentification àadopter
: le plus simple et le moins sécurité (tous les
mots de passe circulent en clair) . Ce n'est malheureusement pas
un choix délibéré mais parce que c'est le
seul protocole compatible avec la majorité des brouteurs.Pour
d'avantage de sécurité, une zone protégée
doit etre placée sur unserveur sécurisé (HTTPS),
si votre hébergeur en dispose.
La directive
"Satisfy" prend toute son importance
lorsque plusieurs critères déterminent l'accès
à un
fichier. Elle permet en effet de choisir si un seul
critère ("any") est déterminant, ou si
tous doivent etre
passés en revue("all").
Ici nous avons
deux principaux critères pour que
l'accès à un fichier soit autorisé ou refusé
:
- L'extension
du fichier, héritage directe du fichier
.htaccess du répertoire précédent, dans notre
exemple.
- L'authentification
du client.
Avec "Satisfy
any", le fait d'etre authentifié suffit à accéder
à un fichier. Par conséquent, il serait possible
de consulter 'abc.doc' bien qu'interdit par les fichiers .htaccess
des répertoires précédent. Nous utilisons
donc systématiquement 'Satisfy all' pour bénéficier
d'une sécurité maximale. Dès l'instant où
le fichier .htpasswd est en place, toute tentative d'accès
au répertoire se solde immédiatement par l'apparition
d'une boite d'authentification. Encore faut-il créer la
liste des utilisateurs valides pour que l'authentification dispose
d'un réel intéret.
La création des listes d'utilisateurs.
Les paires
nom d'utilisateur/mot de passe sont
placées dans le fichier.htpasswd sous une forme
très basique puisqu'il s'agit d'une suite de ligne de
la forme :
nom d'utilisateur:mot
de passe hashé
Le mot de
passe n'est pas stocké en clair, mais
sous la forme d'un nombre, résultant d'une fonction
à sens unique appliquée aux caractères qui
composent le code.
Les
fonctions les plus courantes sont :
- Le DES.
C'est une fonction historique, à éviter de
nos jours. D'une partelle est très rapide à décrypter,
en essayant toutes les possibilités à l'aide des
nombreux outils destinés à cet effet. Ensuite, elle
limite la taille des mots de passe à 8 caractères,
ce qui diminue encore son intéret.Encore plus genant :
cette fonction n'existe pas sur tous les systèmes. Elle
a été définitivement abandonnée par
certaines variantes d 'Unix et aucun serveur sous Windows ne l'implémente.
C'est pourquoi le serveur Apache la déconseille depuis
4 ans (date de la sortie de la version 1.3, avec de nouvelles
fonctions pour remplacer le DES) . En utilisant le DES, vos fichiers
.htpasswd peuvent s'arreter de fonctionner du jour au lendemain,
si l'hébergeur effectue une mise à jour logicielle.
- Le MD5. Cette fonction est bien plus sure que la
précédente et elle est reconnue par un grand nombre
de logiciels. Néanmoins, elle est employé
différemment selon les serveurs. Ainsi, Apache y
ajoute une graine qui la rend incompatible avec
d'autres logiciels qui se basent pourtant sur la
meme fonction.
- Le SHA1. Cette fonction, bien qu'inventée par la NSA,
est considérée comme la plus sure à l'heure
actuelle. Elle a l'avantage d'etre universelle car totalement
compatible avec la plupart des serveurs web, FTP, LDAP, mail...
Voici à quoi ressemble le couple nom d'utilisateur/mot
de passe hashé d'un fichier .htpasswd avec ces trois cryptages
:
DES (56 bits) : toto:Z61EVRs6PJ34Q
MD5 (128 bits) : toto:$apr1$fEIqI/..$tFh.ivIKnp0paKX0yrP5a0
SHA1 (160 bits): toto:{SHA}C5wmJdwh7wX2rU3fR8XyA4N6oyw=
Pour appliquer la fonction SHA1 à un mot de passe et obtenir
un résultat pret à etre inséré dans
un fichier
.htpasswd, on peut utiliser le moduleDigest::SHA1 de
Perl (méthode b64digest) . Le site http://www.hotlinker.net/htpasswd.html
permet aussi
d'effectuer cette opération en ligne. Il ne reste alors
plus qu'à faire un copier/coller versle fichier .htpasswd.
Le fichier
final doit ressembler à ce qui suit :
john:{SHA}ny/rDx70JbKS8vlLyEgklN9DBBM=
richard:$apr1$6cDm4/..$PKCJpyjbBxD/6z.n.n81h.
ellaine:{SHA}uRTmG2uVa0vB8QX5av9U6wqSaiA=
ally:{SHA}/QQDbgFX9EUSDf/T9CRZ4h+0lz4=
Comment se prémunir contre les 'hotlinks'.
- Les hotlinks
: présentation et exploitation.
Le web regorge
de ressources éparpillées aux quatre coins du monde.
Les liens, grace à qui voyager d'un site à l'autre
s'effectue en toute transparence, sont les fondements meme de
la toile. Pourtant,
ces fameux liens se révèlent parfois d'une trop
grande transparence. Prenons l'exemple d'une simple image. Au
milieu du code HTML composant la page web proprement dite, elle
est forcément symbolisée par un lien. Defait, l'image
en question peut provenir du meme serveur, ou bien etre localisée
sur un site distant. Pour le client, le résultat est identique
:il voit l'image et non la source (certes, il pourrait identifier
la source,mais combien de visiteurs prennent de telles precautions?)
. Là réside tout le problème du "hotlink"
: le droit au lien sur une ressource n'est pas forcément
voulu par le propriétaire de celle-ci. Et pourtant,c'est
lui qui devra fournir le contenu et la bande passante, bien que
le site le présentant puisse appartenir à un concurrent
direct. Concrètement, les moyens d'effectuer des "hotlinks"
sont nombreux : - Référence directe à une
image :
<img src="http://ressource.externe">
- Simple lien
:
<a href="http://ressource.externe"> <imgsrc="http://serveur.réel"></a>
. Par exemple, le serveur réel peut présenter
une image réduite d'une vidéo. Mais lorsque le visiteur
va cliquer sur cette image pour télécharger la vidéo
complète, le transfert s'effectuera à partir d'un
serveur tiers. -
Javascript. -
Frames (idéales pour faire croire à un visiteur
qu'il est toujours sur lememe site) .
- DHTML. Il
est pourtant essentiel qu'un site soit protégé contre
les liens non autorisés. Une vidéo "hotlinkée"
par différents sites risque de rapidement écrouler
le serveur hébergeant celle-ci. Pourtant, aucun des visiteurs
n'aura pris connaissance de son site originel ! Le manque à
gagner est donc réel.
Le mythe du "Referer".
Lorsqu'un client web se connecte à un site, il est censé
transmettre au serveur un "refefer", qui caractérise
la page précédente sur lequel se trouvait le visiteur.
Plus exactement, le champs "referer" des requetes HTTP
effectuées par un client web peut contenir la page où
se situe le lien qui a conduit à la page demandée.
Une
idée pour éviter les hotlinks consiste donc à
tester ce "referer" et à refuser toute connexion
en provenance d'un site inconnu. On peut effectuer ceci dans un
fichier .htaccess . Pour citer la FAQ officielle du groupe de
discussion de référence pour les webmasters, "alt.www.webmaster",
disponiblesur http://www.aww-faq.org
: How
can I stop someone from hot-linking to my images?
You can use
.htaccess if you are using mod_rewrite. Add the following to.htaccess
:
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://domain.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^ http://www.domain.com/.*$ [NC]
RewriteRule .*\.gif$ - [F]
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://domain.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^ http://www.domain.com/.*$ [NC]
RewriteRule . *\.jpg$ - [F]>>
Ces lignes ont pour effet d'interdire l'envoi d'images gif et
jpg si le referer n'est pas domain.com,lorsque le referer n'existe
pas, l'acces est malgré tout autorisé (faute de
quoi le site serait inaccessible derriere certains firewalls ou
avec certains brouteurs. De meme, cette precaution est indispensable
pour que les visiteurs puissent ajouter les pages dans leurs listes
de favoris). Beaucoup de webmasters ont recopié ces quelques
lignes sans meme lire la suite, pourtant fort intéressante.
<<Please note that this sort of protection is very weak.
Hotlinking will still work if the linking site is on an SSL encrypted
page, because all clients clear the referer when an HTTP page
is called from an HTTPS one. Also, clients can use simple personal
proxies that transparently rewrite referers to match your rules,
in order to bypass all these filters. An example is Hotlinker
(www.hotlinker.net). These tricks are already widely used for
porn sites. The best way to go is to use cookies (like PHPsessions)
or cryptography.>> En résumé : - Tout
site utilisant le protocole HTTPS peut librement effectuer des
liens car lorsque le brouteur vient d'un tel site, le 'referer'
est volontairement retiré, afin de ne jamais transmettre
d'éventuelles informations confidentielles. Un autre intéret
du protocole HTTPS est qu'il n'est pas pris en charge par les
moteurs de recherche. Il devient ainsi très difficile,
pour un site hotlinké, d'en déceler l'origine. C'est
pourquoi on trouve de plus en plus de sites TGP officieux en HTTPS,
bien qu'ils n'y pratiquent aucun commerce électronique.
- Vu que le client web fixe lui-meme le contenu du referer, il
peut tout à fait le modifier à sa guise. Des petits
programmes s'en chargent d'ailleurs très bien et recopient
simplement le nom du site dont fait partie le lien. Le referer
est donc toujours valide. Ajoutons à cela que l'utilisation
du module mod_rewrite d'Apache a connu de sérieux problèmes
de sécurité et augmente forcément la charge
d'un serveur. Le referer n'est donc qu'indicatif. Il peut etre
intéressant lors d'analyses statistiques, mais il doit
définitivement etre écarté pour tout besoin
d'authentification ou de vérification.
Les identifiants uniques.
- L'envoi
de cookies. Nous
devons donc trouver un moyen d'identifier un visiteur et de s'assurer
qu'avant de télécharger un fichier, il est bien
passé par un hemin"conventionnel". En l'occurrence
par les pages du site où se situent les liens vers ce fichier.
Une
première approche, assez simple, consiste à employer
des cookies. Nous allons l'illustrer avec un site fictif, www.42.sex
. Le déroulement des opérations est le suivant :
- Le
visiteur se connecte sur le site http://www.42.sex . -
Une valeur aléatoire est générée.
On la transmet au client qui va la conserver sous la forme d'un
cookie. Coté serveur, on mémorise aussi la valeur.
- Avant
d'envoyer un fichier susceptible d'etre hotlinké, on vérifie
que le cookie reçu soit présent dans la base des
valeurs précédemment envoyées.Dans le cas
contraire, le fichier est hotlinké. Les
client peut alors librement accéder au site, et meme enregistrer
les liens dans ses favoris sans aucun problème.
Inconvénients
du système :
- Si
le client efface ses cookies, il devra repasser par la case départ
pour obtenir un nouveau cookie.
- Tous
les cookies doivent etre conservées sur le
serveur. Cela prend de l'espace disque, et un petit
malin peut demander un grand nombre de cookies
au serveur jusqu'à saturation de l'espace.
- Les cookies
ne sont pas toujours bien tolérés par les visiteurs,
car ce sont avant tout des mouchards. - Dès l'instant où
un visiteur a un cookie valide pour un site, les hotlinks vers
ce site deviennent possibles pour lui. En pratique, les risques
sont minimes car cette situation se rencontre très rarement.
Un exemple d'implémentation en PHP.
Voici un exemple, en PHP, d'implémentation de la méthode
anti-hotlinks présentée ci-dessus.
Pour en tirer partie, il suffit d'organiser son site en
séparant son contenu(pages HTML, photos, vidéos,
fichiers à télécharger...) en deux catégories
:
- Celles
qui ne présentent aucun risque à etre liées,
telles que les pages d'accueil.
- Celles
qui doivent au contraire n'etre accessible que
si l'une des pages précédentes a préalablement
été
visitée.
Les pages de la première catégorie vont avoir pour
but d'envoyer un cookie au client, si celui-ci n'en dispose pas
encore. Pour cela, on dispose des fonctions withcookie() et send_withcookie()
définies dans le listing précédent. La premiere
envoie un cookie au client si celui-ci n'en dispose pas encore.
La seconde est identique, mais transmet en plus le contenu d'un
fichier. Les pages de la seconde catégorie vont d'abord
valider la présence du cookie avant d'envoyer tout contenu.
Pour cela, nous avons défini deux fonctions : checkcookie()
qui se contente d'effectuer une vérification, etsend_checkcookie()
qui envoit le contenu d'un fichier après vérification.
Voyons
comment transformer un site traditionnel en un site protégé
contre les hotlinks à l'aide de ces fonctions.
Nous
allons partir d'un site traditionnel, dont la structure est la
suivante :
- /index.html
: page d'accueil.
- /videos.html : la page presentant les videos disponibles sur
le site.
- /videos/video1.mpeg et /videos/video2.mpeg : deux
videos.
But du jeu
: interdire l'acces a la page des videos et
aux videos proprement dites, sauf si la page d'accueil
a préalablement été visitée. La
première chose à faire
est de créer un répertoire dont l'accès par
le web sera totalement interdit. Le répertoire peut etre
situé en dehors de la hiérarchie des pages web,
ou bien etre protégé par un fichier .htaccess .Dans
notre exemple, ce répertoire va etre /private.
Nous allons
y placer un premier fichier, nommé
antilink.php, avec le contenudu précédent listing.
Il contient toutes les fonctions à réutiliser par
la suite.
Le fichier index.html va ensuite etre renommé index.php.
Deux modifications vont etre apportées à son contenu
:
- Le
lien vers videos.html va etre remplacé par un lien
vers videos.php .
- Les lignes
suivantes vont etre ajoutées tout au début du fichier
:
<script language="php">require('../private/antilink.php');
withcookie();</script>
Le but de ces ligne est simplement d'envoyer un cookie au client.
Celui-ci est en effet indispensable pour qu'il puisse visiter
les pages suivantes. Ensuite,
le fichier videos.html va à son tour etre renommé
videos.php . Là encore, deux modifications sont à
apporter :
- Les
liens vers /videos/video1.mpeg et
/videos/video2.mpeg vont etre remplacés par des liens vers
/videos/video1.php et /videos/video2.php .
|

- Les lignes
suivantes vont etre ajoutées au début :
<script
language="php">require('../private/antilink.php');
checkcookie();</script>
Ces lignes vérifient la présence d'un cookie valide.
Dans le cas contraire, le contenu de la page n'est pas affiché.
Si le contenu du répertoire /videos est librement accessible,
aucune protection contre les hotlinks n'est possible. C'est pourquoi
nous allons déplacer toutes les vidéos vers un répertoire
privé, par exemple le répertoire /private. L'astuce
consiste en effet à défendre au client d'y accéder
sans passer par un script PHP vérifiant les cookies. C'est
pourquoi nous allons placer dans le répertoire /videos deux
scripts, video1.php et video2.php. Ceux-ci vont simplement appeler
la fonction send_checkcookie(), qui controle les cookies puis, si
tout semble en règle, envoit le contenu d'un fichier (ici
: une vidéo) .
Pour le visiteur, cela ne fait aucune différence.
Bien que le lien pointe vers un fichier '.php', son brouteur va
bien comprendre qu'il s'agit d'une vidéo. Pourquoi? Parce
que sur le web, un fichier n'est jamais transmis sans préalablement
en préciser la nature. Notre fonction send_checkcookie()
va donc attendre deux arguments : le type de fichier, et son nom.
Voici quelques valeurs possibles pour le type, dont les noms parlent
d'eux memes :
audio/mpegimage/gifimage/pngimage/
jpegtext/htmltext/plaintext/cssvideo/mpegvideo/ quicktimevideo/x-msvideovideo/vnd.rn-realvideo
Voici donc à quoi va ressembler le
fichier videos/video1.php : <script language="php">require('../../private/antilink.php'
);send_checkcookie('video/mpeg', '../../private/video1 .mpeg');</script>
Le fichier videos/video2.php est bien entendu
à créer de façon analogue.
La cryptographie : une autre approche
de la lutte contre les hotlinks.
L'approche précédente a le mérite d'etre simple
à mettre en oeuvre. La migration d'un site classique vers
cette architecture protégée ne demandepas énormément
de temps. Une autre approche est néanmoins fort intéressante
: la cryptographie. En voici les avantages : - Aucun espace disque
nécessaire pour un nombre illimité de visiteurs distincts.
- Cette solution fonctionne sans cookie. - La visite peut s'effectuer
avec différents brouteurs sans perte d'authentification.
- Meme après avoir visité un site dans les règles,
il est impossible d'yeffectuer des hotlinks à partir d'autressites
(contrairement à la méthodedes cookies) .
- Conséquence directe : le site ne peut
etre la cible d'un cross-scripting.C'est une arme efficace pour
éviter la triche sur des outils de vote. Et du coté
des inconvénients : - La méthode présentée
ici se base sur l'adresse IP du client. S'il ne dispose pas d'une
IP fixe, l'enregistrement d'une page protégée dans
les favoris ne fonctionnera pas. - Le déploiement est un
peu plus complexe que celui de la méthode par cookie. Mieux
vaut adapter dès le départ son site à une protection
cryptographique. Le principe est d'attribuer des adresses personnalisés
à chaque client.Ainsi, pour atteindre une meme image, deux
personnes devront se rendre à des URL totalement différentes,
et non interchangeables. Les hotlinks deviennent de ce fait impossible,
puisqu'un lien effectué par un webmaster ne serait valide
que... pour sa propre machine.
La protection repose sur les fonctions à
sens unique, que nous avons déjà évoquées
pour les mots de passe. Soit F une telle fonction (qui peut etreMD5,
SHA1, RMD160, ...) . Si l'on calcule F(X), on obtient un nombre
caractéristique de X, mais à partir duquel il est
impossible de retrouver X sans essayer toutes les possibilités
(ce qui peut prendre un temps colossal avec des fonctions lentes
dont le résultat est sur 160 bits) . Mais il n'y a bien entendu
qu'un seul résultat possible pour F(X) .
Imaginons maintenant le scénario suivant
: - Le client demande à charger la page d'accueil. - Le serveur
connait l'adresse IP de tous les clients qui s'y connectent. Il
calcule F(IP) et, dans la page d'accueil, il ajoute '&key=<xxxxx>'
après chaque lien (<xxxxx> étant le résultat
de F(IP)) . - Lorsque le client va cliquer sur un lie n, il va transmettre
une valeur qui le caractérise, ici l'application d'une fonction
à son adresse IP. Le serveur connait l'adresse IP du client
et peut recalculer F(IP) à tout moment, et comparer le résultat
avec la valeur de 'key' envoyée par le client. Si les valeurs
diffèrent, il y a clairement un problème : le client
n'a pas renvoyé la valeur initialement proposée par
le serveur. Nous avons donc affaire à un hotlink, car le
lien ne peut provenir du serveur d'origine. Cette méthode
est néanmoins très limitée, car si F est connue
du client, il peut recalculer F(IP) sans problème. Le hotlink
devient alors possible, car le site responsable du hotlink peut
regénérer les liens à la volée, d'après
l'adresse IP du visiteur. Nous allons rendre cela impossible en
ajoutant une clef secrete à l'adresse IP. C'est à
dire qu'au lieu d'envoyer F(IP), le serveur web va calculer F(<abcdef>
(+) IP) . <abcdef> est un nombre secret, présent sur
le serveur, mais qui ne va jamais etre transmis aux clients. Cette
clef secrète étant fixe, le serveur est toujours en
mesure de recalculer la clef d'authentification. En revanche, cette
opération devient impossible pour un serveur tiers, qui peut
disposer de l'adresse IP du client, mais pas de la clef du serveur
d'origine. Le hotlink est donc impossible. Pour compliquer les choses,
nous pouvons faire en sorte que deux visites sur le meme site d'un
meme client (à partir d'une meme adresse IP) provoque malgré
tout un échange de clefs totallement différentes.
Cela en ajoutant une "graine" aléatoire : la clef
d'authentification devient alors F(<valeuraléatoire>
(+) <abcdef> (+) IP) . Pour que le serveur puisse recalculer
la clef par la suite, la valeur aléatoire doit en revanche
obligatoirement etre transmise dans les liens, au meme titre que
la clef. La seule difficulté de ce procédé
consiste à rendre toutes les pages HTML dynamiques puisque
les liens doivent etre calculés en fonction du client.Pour
y parvenir, on peut mélanger PHP et HTML :
<script language="php">echo '<a href="/videos/
video1.php?key=' . urlencode($key) . '">';</script>
<img src="thumbnail.jpg" alt="sous la douche"
/></a>
On peut aussi écrire des pages HTML traditionnelles,
mais comportant des balises qui seront dynamiquement remplacées
par la clef. Ainsi,'videos.html' sera dans un répertoire
inaccessible, mais 'videos.php' va charger ce fichier, remplacer
les balises par la clef, et afficher le résultat. Une implémentation
complète de cette technique, baptisée Antilink, est
disponible à l'adresse suivante :
ftp://ftp.fr.pureftpd.org/pure-ftpd/antilink/ Elle
est écrite en langage C (mais se destine uniquement aux systèmes
Unix),utilise la fonction SHA1 et effectue les substitutions de
balises. Les clefs d'authentification sont calculées d'après
l'adresse IP, une graine de 64bits et une clef secrète de
1024 bits. Elle comporte quatre programmes utilisant l'interface
CGI, compatible avec n'importe quel serveur web : - antilink-sendkey.cgi
: envoit le contenu d'un fichier et la clef d'authentification au
client. - antilink-checkkey.cgi : vérifie la validité
de la clef envoyée par le client, charge un fichier, substitue
la clef à la balise <KEY /> et envoit le résultat.
- antilink-checkkey-raw.cgi : vérifie la validité
de la clef et envoie le contenu d'un fichier sans substitution (par
exemple pour une image, une vidéo...) . - antilink-checkkey-cgi.cgi
: lance un script CGI quelconque après avoir vérifie
la validité de la clef. A l'heure où ces lignes sont
écrites, Antilink fonctionne parfaitement, mais manque cruellement
de documentation et n'est peut-etre pas très aisé
à installer. Nous y reviendrons donc dans le prochain numéro
d'ABV, exemples à l'appui, éventuellement complétés
d'une implémentation plus simple en PHP. - Introduction
à la programmation sécurisée. A la base
de tout site dynamique se trouvent des scripts. Qu'ils soient écrits
en Perl, en PHP, en Pike, en Python, en Shell, ou en OCaml, ils
ont tous le meme but : générer un contenu d'après
un ensemble de données,combinées aux requetes effectuées
par les clients. Ce dernier élément est particulièrement
sensible. En fonction des URL choisies, des cookies échangés,
des cases cochées ou des formulaires remplis, les scripts
vont effectuer un traitement spécifique. Dans la plupart
des cas, le traitement suit la procédure logique établie
par le programmeur. Mais que se passe-t-il en cas de bogue? Et à
plus forte raison si le client choisit volontairement des paramètres
destinés à faire ressortir ces bogues? Dans le meilleur
des cas, le visiteur obtient un message d'erreur. Dans le pire des
cas, le visiteur a pris le controle de votre serveur. Voici donc
quelques conseils pour éviter les erreurs les plus basiques,
mais qui sont malheureusement aussi les plus fréquentes.
Les valeurs aléatoires.
En informatique, la notion de valeurs aléatoires
est très subjective. Ces valeurs proviennent en effet toujours
du résultat de fonctions mathématiques. Et dès
l'instant où les paramètres de ces fonctions sont
connues, les valeurs sont évidemment totalement prévisibles.
Pour une utilisation courante (sélection aléatoire
de bannières, répartition de charge, ...) une suite
prévisible de valeurs aléatoires n'est pas très
génante. En revanche, pour du commerce électronique,
des jeux primés ou toute forme d'authentification, il est
hors de question de s'en tenir à un générateur
de nombre pseudo-aléatoires. Il faut donc choisir des sources
d'entropie particulièrement imprévisibles.
La date : une source intemporelle d'entropie.
Dans quasiment tous les langages de programmation se trouve une
fonction destinée à générer des nombres
pseudo-aléatoires. Elle fait appel à une fonction
mathématique dont la répartition du résultat
est (ou devrait etre) uniforme. Lorsqu'une nouvelle valeur pseudo-aléatoire
doit etre tirée, on calcule F(n+o), ou 'n' est le nombre
de tirages effectués par le script courant, et 'o' une graine,
destinée à ce que deux invocations du meme programme
donnent des résultats différents. 'n' est généralement
très faible. Si une seule valeur aléatoire est tirée
(par exemple pour envoyer un cookie), 'n' sera meme toujours nul.
La difficulté de prédire les valeurs aléatoires
à venir ne dépend donc que d'un seul facteur : 'o'.
Il doit donc sans cesse changer pour etre une source d'entropie
suffisante. Et c'est là où le bat blesse : la plupart
des programmeurs utilisent simplement la date courante comme graine
avec des fragments de code source tels que : srand(time());
C'est une très mauvaise idée,
pour deux raisons :
- ainsi lue, la date a une précision maximale d'une seconde.
Deux connexions successives auront par exemple le meme cookie.
- il est trivial de deviner la graine, meme avec unéchantillon
de valeurs pseudo-aléatoires très réduit, les
machines étant censées etre à l'heure. La documentation
de PHP conseille d'y ajouter les fractions de secondes, à
l'aide de la fonction microtime() :
function make_seed() { list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);}srand(make_seed());
Un traitement similaire peut etre effectué dans les autres
langages (par exemple à l'aide de syscall(&SYS_gettimeofday...)
en Perl, gettimeofday() en C) . Malheureusement, cette méthode
reste loin d'etre infaillible. Selon les systèmes, la précision
de l'horloge varie. Elle n'est parfois que de deux secondes, ce
qui rend la fonction microtime() guère plus fiable que time()
. Pour y pallier, les systèmes d'exploitation modernes disposent
de générateurs de nombres aléatoires plus surs,
dont la graine est fixée au démarrage du système.
'n' (le nombre de tirage) est incrémenté à
chaque consultation du générateur quelqu'en soit le
programme appellant. On y accède en lisant simplement le
contenu d'un fichier, chaque byte lu étant aléatoire.
Les sources d'entropie employées par le système pour
ces générateurs peuvent etre très variées
: - Traffic réseau (bien que ce soit une source souvent contestée
par les puristes) . - Date du dernier reboot. - Déplacements
de la souris, touches frappées au clavier. - Etat matériel
: interruptions, contenu des registres, ... - Sources imprévisibles
: radioactivité, mouvements des électrons, ... La
méthode la plus courante consiste à échantillonner
deux diodes Zener différentes (valeur '1' au delà
d'un certain seuil, 0 dans le cas contraire) et à les combiner
par une porte logique pour obtenir un signal binaire imprévisible
(en général une porte AND suivie d'une porte NOT pour
éviter le cas où les deux diodes ont excédé
le seuil simultanément à cause de facteurs externes)
. Les cartes-mères telles que celles basées sur un
chipset i810 disposent d'un tel circuit, mais tous les systèmes
d'exploitation n'en tirent pas forcément profit. Pour accéder
aux valeurs aléatoires, on dispose de plusieurs fichiers,
certains n'étant pas présents sur tous les systèmes,
ou nécessitant l'installation d'un module annexe (exemple:
Solaris) : - /dev/srandom : c'est le générateur le
plus lent, mais le plus sur. Il ne se base que sur des sources d'entropie
fiable et va attendre que des données soient disponibles
si nécessaire. Les données ne sont de plus jamais
directement transmises sans passer par une fonction à sens
unique (souventMD5) . - /dev/random : c'est soit une interface vers
les modules matériels de génération de nombres
aléatoires, soit l'équivalent de /dev/srandom. C'est
donc aussi une source sure. - /dev/arandom : identique à
/dev/srandom, mais dont la rapidité est garantie : lorsqu'il
n'y a plus de données disponibles dans une source d'entropie,
on réalimente le générateur de nombre aléatoires
à l'aide des résultats précédents auquels
on applique une fonction tels qu'un RC4./dev/arandom est le meilleur
compromis sécurité/rapidité, fiable aussi bien
pour la génération de certificats que pour une utilisation
intense (ex: génération de cookies) . - /dev/urandom
: générateur très rapide, mais moins sur que
les précédents.Lorsqu'il n'y a plus de données
dans la source d'entropie, la suite est extrapolée. Pour
le système anti-hotlinks à base de cookies présenté
plus haut,ce générateur est suffisant car un cookie
ne peut de toutes façons etre attribué qu'à
partir du domaine qui le concerne. - /dev/prandom : le plus rapide
des générateurs, basé sur une graine et une
simple fonction. Il ne garantie en revanche aucune sécurité.
Voici une simple fonction PHP retournant un byte aléatoire
(en principe entre 0 et 255) à l'aide de ces interfaces,
ou -1 en cas d'erreur :
function getrandbyte() { if (($rd = fopen('/dev/arandom',
'rb')) == FALSE) { return -1; } $v = fread($rd, 1); fclose($rd);
if (strlen($v) != 1) { return -1; } return ord($v);}
Essayez cette fonction avec les différents
générateurs (/dev/random,/dev/urandom, ...) . Si la
fonction renvoit systématiquement -1 avec l'un d'entre eux,
c'est que le système hébergeant le serveur web ne
l'implémente pas. Si le système ne dispose pas de
/dev/arandom, que /dev/srandom (ou/dev/random) se révèle
trop lent, et que l'on a malgré tout besoin de valeurs sures,
une solution consiste à mélanger plusieurs sources
non sures, mais distinctes, par exemple /dev/urandom et un appel
au générateur classique (par exemple rand(), après
en avoir initialisé la graine) :
function getrandbyte2() { if (($v = getrandbyte())
== -1) { return -1; } return hexdec(substr(md5($v . rand()), 0,
2));}
Les sessions PHP.
L'une des facilités les plus
appréciées du langage PHP est la possibilité
qu'il offre de sauvegarder un ensemble de variables. Cet ensemble
est identifié par un nombre unique, qu'il est simple de passer
ensuite au client pour conserver le contenu des variables d'un lien
à l'autre. Les variables peuvent etre stockées de
différentes façons : - Dans de simples fichiers. C'est
le cas le plus courant. - En mémoire partagée. Cette
solution est très rapide, mais malheureusement pas très
fiable avec les versions actuelles de PHP. - Dans une base SQL,
ce qui peut hélas se révéler assez lent. Chez
la plupart des hébergeurs, c'est la première solution
qui est retenue par défaut. A chaque session PHP, un fichier
est crée. Et faute de consignes adéquates, les fichiers
de tous les clients d'un serveur mutualisé sont stockés
dans un unique répertoire. Deux problèmes se présente
alors : - Beaucoup de systèmes de fichiers voient leurs performances
descendre en flèche lorsqu'il s'agit de naviguer à
l'intérieur d'un répertoire comportant beaucoup d'entrées.
De fait, l'accès aux sites est considérablement ralenti.
- Ce répertoire partagé (généralement
/tmp) est souvent lisible par tous les comptes d'un serveur mutualisé.
Ce qui signifie qu'un client peut voler les sessions de tous les
autres sites. Autre problème couremment rencontré
avec les sessions PHP : la prédicabilitéde ceux-ci.
Comme nous venons de le voir ci-dessus, un générateur
de nombre aléatoires doit produire des résultats imprévisibles.
C'est d'autant plus vrai lorsque les variables d'une session peuvent
contenir des mots de passe, des coordonnées ou des commandes
en cours.Par défaut, celui de PHP n'est malheureusement pas
sur, car il se base simplement sur la date. Nous allons remédier
à tous ces problèmes concernant les sessions en prenant
deux mesures : - En indiquant à PHP d'employer une source
fiable comme /dev/arandom. - En stockant les fichiers dans un répertoire
dédié au site concerné. Pour compléter,
nous allons demander à PHP de vérifier assez souvent
si de vieux fichiers de session ne seraient pas bons pour la poubelle.
Cela évite de sérieux ralentissements sur les sites
recevant beaucoup de traffic. Pour ce faire, les lignes suivantes
sont à adapter et à ajouter au début d'un fichier
PHP exploitant les sessions
:ini_alter("session.entropy_file",
"/dev/arandom"); ini_alter("session.entropy_length",
"16");ini_alter ("session.gc_probability", "42");session_save_path
("/home/john/cookies");
Le répertoire de sauvegarde (ici "/home/john/cookies")
doit etre préalablement crée, et il doit etre accessible
en lecture et écriture par l'utilisateur sous lequel les
scripts PHP sont exécutés. En principe ils'agit de
vous-meme. Vous pouvez alors retirer les droits d'accès aux
autres utilisateurs.
Les variables externes.
En dehors des sessions, PHP disposait d'une facilité très
employée pour accéder aux variables transmises par
un client : une copie de celles-ci étaient stockée
dans des variables globales. Ainsi, le contenu du champs PRENOM
d'un formulaire était accessible comme une simple variable
nommée $PRENOM. Ce fut une idée fort regrettable,
car une utilisation imprudente de ce mécanisme a conduit
des centaines de scripts à posséder des failles de
sécurité triviales à mettre en oeuvre. Prenons
l'exemple d'une variable 'user_ok' dont la valeur serait TRUE après
authentification réussie de l'utilisateur :
if (user_auth()) { user_ok = TRUE;}
Rien n'empeche un utilisateur malveillant d'accéder au site
en passant une variable 'user_ok' avec le contenu qu'il désire.
Par exemple simplement en ajoutant '&user_ok=1' à l'URL
à laquelle il se rend. Résultat : l'utilisateur va
pouvoir naviguer sur le site sans authentification préalable.
Autre exemple courant : si une session conserve le nom du visiteur
connecté dans une telle variable, rien ne l'empèche
de modifier cette variable pour prendre l'identité de quelqu'un
d'autre. Pour cette raison, depuis la version 4.1.0 de PHP, ce comportement
est désactivé par défaut. Libre à l'hébergeur
de l'activer explicitement pour conserver une compatibilité
ascendante avec les scripts mal écrits (et c'est malheureusement
le choix qui est effectué la plupart du temps) . Mais il
est prévu à court terme de retirer totalement cette
fonctionnalité de l'interpréteur PHP. Pour accéder
à une variable en provenance d'un client web, on doit désormais
impérativement faire appel à quatre tableaux en PHP
: - $_GET : qui contient toutes les variables passées parla
méthode 'GET'. - $_POST : qui contient toutes les variables
passées par la méthode 'POST'. - $_COOKIE : qui contient
les cookies. - $_REQUEST : qui contient toutes les informations
des trois tableaux précédents. Il regroupe donc toutesles
variables envoyées par le client,que celui-ci est susceptible
de modifier à sa guise. De cette façon, les variables
clients sont totalement distinctes des variables globales qui doivent
rester internes au script PHP. Voici une simple page HTML comportant
un formulaire :
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html> <head> <title>Formulaire</title>
</head> <body> <form action="getemail.php"
method="post"> <input type="text" name="email"
size="32"> <input type="submit"> </form>
</body></html>
Nous disposons ici d'une variable cliente 'email' envoyée
par la méthode'POST'. Voici donc comment y accéderdans
un script PHP :
$email = $_POST['email'];
L'interruption de scripts PHP.
Une chose est à savoir : si un visiteur
appuie sur le bouton 'Stop' de son brouteur pendant l'exécution
d'un script PHP, et que ce script a la bonne idée d'essayer
ensuite d'envoyer la moindre donnée au client, le script
va prendre immédiatement fin. S'il restait des requetes SQL
importantes à effectuer (exemple: enregistrement de l'IP
du client après avoir pris encompte un vote), elle ne seront
malheureusement jamais lancées. Si des fichiers temporaires
ont été crées, ils ne seront pas effacés.
Nous allons ici présenter une solution en PHP, mais tous
les autres langagesde scripts ont le meme comportement. En Perl
ou en C, il faudra par exemple interceper les signaux SIGPIPE et
SIGKILL pour éviter tout désagréments. PHP
dispose d'une fonction fort méconnue et pourtant indispensable
dans ce type de situation : register_shutdown_function() . Son but
est d'enregistrer une fonction qui sera dans tous les cas appellée
à la fin d'un script PHP,que celui-ci ait pris fin de façon
toute à fait normale, ou qu'il ait été plus
violemment interrompu. Plutot que d'employer la structure suivante
: - ouverture de bases, création de fichiers temporaires,
allocation de mémoire. - traitement. - libération
de mémoire, effacement des fichiers temporaires, fermeture
debases. Il est infiniment plus fiable de commencer par enregistrer
la fonction chargée du nettoyage des opérations, qui
sera forcément appelée au moment propice : - enregistrement
de la fonction qui libere la mémoire, efface les fichiers
temporaires et ferme les bases. - ouverture de bases, création
de fichiers temporaires, allocation de mémoire. - traitement.
Ainsi, meme si le client met prématurément fin à
une connexion (que ce soit en cliquant sur Stop ou suite à
des problèmes réseau), tout reste cohérent
toujours coté serveur. Voici un simple extrait de code source
qui annule la transaction MySQL encours si le script a été
interrompu avant la fin du traitement :
<script language="php"> # Fonction de 'nettoyage'
appelée à la fin du script (meme si celui-ci a été#
interrompu par le client) . function cleanup() { global $sql_ok;
global $sql_id; if (!isset($sql_id)) { return; } if ($sql_ok !=
FALSE) { mysql_unbuffered_query('ROLLBACK', $sql_id); } mysql_close($sql_id);}
# sql_ok indique s'il est nécessaire d'effectuer ou non un
rollback $sql_ok = FALSE; # Enregistrement de la fonction de nettoyage.
register_shutdown_function('cleanup'); # Script proprement dit.
if (($sql_id = mysql_pconnect ('sql', getenv('SQL_USER'), getenv
('SQL_PASSWORD'))) == FALSE) { echo mysql_error(); exit;} mysql_unbuffered_query('SET
AUTOCOMMIT=0', $sql_id); # ... divers traitements, affichages et
requetes ... $sql_ok = TRUE; </script>
Les dangers du cross-scripting.
Présentation du cross-scripting (CSS).
L'attaque concernant les sites web la plus en vogue actuellement
se cache derriere une abbréviation de trois lettres : CSS,
pour "cross-scripting"(sans rapport avec les feuilles
de style) . Et pour cause : tous les sites sont potentiellement
vulnérables, indépendemment des scripts, des logiciels,des
systèmes et du matériel sur lesquels ils tournent.
Le cross-scriptingad met aussi un grand nombre de variantes, et
de nouvelles sont découvertes chaque jour. Quelques paragraphes
se révèlent donc bien maigres pour cerner ce problème,
mais nous allons en décrire les principes afin de sensibiliser
les webmasters. Le problème est sérieux puisque meme
Microsoft, Netscape,Yahoo, CNN et Google en ont récemment
subit les facheuses conséquences.
Prenons l'exemple d'un forum de discussion. Chacun
est libre d'y poster des messages publiquement consultables. Pour
agrémenter ceux-ci, toutes les balises HTML sont autorisées.
Les visiteurs peuvent ainsi placer des images, des effets de style
et... des scripts. Ce dernier point ouvre beaucoup de possibilités.
Par exemple, on peut aisément écrire un javascript
qui va ouvrir une connexion à l'adresse 'http://www.42.sex/<cookie>',
où <cookie>est remplacé par un cookie placé
par le forum. Le propriétaire du site 42.sex n'a plus qu'a
consulter ses fichiers d'historique pour connaitre les cookies des
visiteurs du forum, et se connecter sous leur identité. Toutes
les variables peuvent ainsi etre transmises. Il est aussi courant
d'ouvrir ainsi des popups vers le site d'origine pour que tout client
visualisant le forum devienne aussitot inutilisable, tout en chargeant
simultanément le serveur. Le javascript peut aussi contenir
des adresses de serveurs privés qui ne feront aucune différence
pour les utilisateurs piégés.
Bien, il suffirait donc de retirer les balises
permettantl'exécution de scripts pour etre à l'abris
de telles attaques? Que nenni, le cross-scripting ouvre bien d'autres
portes. Soit http://www.42.sex un site permettant de voter pour
ses sites préférés. Les sites qui y sont référencés
vont n'avoir qu'un seul but : se placer en haut du classement. Seulement
voilà : un seul vote par adresse IP est autorisé.
L'adresse exacte permettant d'enregistrer un vote pour le site 123
est http://www.42.sex/vote.pl?site=123 . Comment obliger un maximum
de personnes à se rendre à cette adresse, en utilisant
d'autres vulnérabilités du forum? D'apparence anodine,
les liens vers des images ont pourtant cette faculté.Il suffit
en effet d'inclure dans le message la balise suivante :
<img width="1" height="1" src="http://www.42.sex/vote.pl?site=123">
Pour que chaque personne visualisant le message se retrouve contre
son gré à voter pour le site 123. Contre son gré
et... sans s'en rendre compte puisqu'à l'écran, en
dehors d'un imperceptible pixel, rien ne parait suspect. Bien...
les scripts et les images doivent donc etre filtres. Est-ce enfin
suffisant? Et non... Car bien entendu des résultats similaires
peuvent etre obtenus avec les frames. Les balises <body> présentent
donc aussi des risques. Le plus sur est donc de partir du principe
qu'aucune balise n'est autorisée,et de tolérer explicitement
celles qui ne présentent vraiment aucun danger(<b>,
<em>, <font>, ...) . Nous avons illustré ceci
avec l'exemple d'un forum, mais toutes les pages dont un utilisateur
peut indirectement modifier le contenu est potentiellement vulnérable.
A titre d'exemple, prenons un formulaire permettant à un
visiteur de s'inscrire sur une liste de diffusion. Il y entre son
nom, son prénom et son email. Ses coordonnées sont
alors enregistrées sur le serveur et une page s'affiche avec
un contenu semblable à ce qui suit : <<<Bonjour
Richard Fish, votre abonnement a bien été enregistré.>>>
Un site tiers peut tout à fait mettre en place un lien qui
va pointer sur le script de traitement de ce site. Par exemple avec
un lien comme celui-ci :
<a href="http://www.42.sex/abo_liste.pl? nom=Richard&prenom=
Fish&email=rf@cagefish.fr"> ...</a>
Si le script n'accepte que la méthode
POST, le site tiers peut tout à fait mettre en place un formulaire
factice. Bien. Il est possible de s'inscrire à la liste de
diffusion de 42.sex à partir d'un autre site. Où sont
les risques ? Faute de protection, ils sont semblables à
ceux du forum évoqués plus haut,car il suffit de placer
des balises HTML dans les variables 'nom' ou' prenom' pour exploiter
les memes failles (exécution de script, liens cachés,
substitution de cookies...) .
Comment s'en protéger ?
Afin d'éviter les conséquences liées au cross-scripting,
des protections doivent etre mises en place : - pour interdire l'affichage
de balises HTML non autorisées, et ce quelqu'en soit la page.
- pour interdire les liens directs sur des scripts sensibles à
partir de sites externes. Le second point revient à éviter
les hotlinks, avec les techniques présentées au début
de cet article. Il n'y a en revanche aucune réponse universelle
au premier point, si ce n'est un maximum de vigilance lors de l'écriture
de scripts. La règle simple a toujours avoir en tete est
: le serveur ne doit jamais etre en mesure d'envoyer à un
client des balises en provenance d'un autre client. Un principe
simple, mais parfois laborieux à appliquer à la lettre
s'il n'a pas été pris en compte dès la conception.
Jedi
|