WWW et la sécurité
Introduction
Le but de ce document est de proposer quelques conseils, faciles à mettre
en oeuvre, relatifs à la sécurité sur le web.
Ces conseils, qui permettent d'augmenter "facilement" la sécurité, s'adressent à
plusieurs catégories d'utilisateurs:
Le but est donc d'éviter de laisser béantes des brèches facilement colmatées ...
Ce document est donc plus un recueil de conseils qu'un cours ou une FAQ sur la sécurité WWW.
Pour cela je vous invite à consulter les documents suivants :
Ainsi que la FAQ WWW.
De quels risques parlons nous ici ?
A partir du moment où un serveur WWW est mis en place, vous ouvrez une vitrine sur votre organisation.
De manière schématique, deux risques se présentent alors à vous :
- que cette vitrine soit utilisée pour "regarder" autre chose que ce que vous avez choisi d'y présenter
- que cette vitrine, ou son contenu, soit modifié à votre insu
Nous allons nous intéresser à la première catégorie de risques et voir comment il est possible de
restreindre ces risques, notamment en réduisant les brèches qui peuvent apparaître suite à la mise en
place d'un serveur WWW.
La deuxième catégorie de risques est rarement aggravée par la mise en place d'un serveur WWW.
Les brèches à combler sont alors du domaine de la sécurité des systèmes informatiques en général.
Envoyez vos suggestions,commentaires et corrections
à Nicolas.Cros@cict.fr.
Vous êtes administrateur de machine hébèrgeant un serveur WWW ...
Voici quelques règles générales à mettre en application sur un système UNIX hébergeant un serveur WWW :
- Limiter le nombre d'utilisateurs déclarés sur le serveur
- S'assurer que les mots de passe des utilisateurs, et notamment les utilisateurs ayant accès à
la hiérarchie du serveur WWW, sont robustes (c'est à dire non "crackables", voir par exemple ce
que nous demandons aux utilisateurs du CICT).
- Ne pas mettre en route des services inutiles.
On peut pour cela analyser le fichier /etc/inetd.conf et
commenter les lignes qui lancent des serveurs inutiles dans la configuration utilisée.
Il faut aussi vérifier quels sont
les services lancés au démarrage de la machine (cf, suivant les constructeurs, /etc/init.d,
/etc/rc*).
- Surveiller régulièrement les journaux (logs) produits tant par le système que par le serveur WWW.
Pour les logs du serveur, rechercher particulièrement les accès
- dont l'URL contient des commandes systèmes
rm , login, /bin/sh, perl,`(backquote)
indiquant une
tentative de faire exécuter à un script CGI des commandes pour lequel il n'a pas été conçu ;
- dont l'URL est très long, trahissant une tentative de dépassement des buffers d'entrée
d'un programme
Vous administrez un serveur WWW ...
Voici une stratégie possible pour la mise en place d'un serveur WWW :
- ne pas faire tourner votre serveur sous root, ou plus exactement ne pas faire tourner les
fils de votre serveur (ceux qui délivrent les pages) sous root. Toutes les actions que réaliseraient
les scripts CGI le seraient alors avec les privilèges de root, donnant ainsi l'accès à tous les recoins
de votre système.
- créer un groupe www et un utilisateur www. Le premier permettra de regrouper l'ensemble des
rédacteurs du serveur, l'utilisateur www sera utilisé pour exécuter les fils du serveur WWW (même si
ce dernier est lancé sous root pour pouvoir écouter sur le port 80).
- utiliser les droits d'accès suivants :
- seul www doit pouvoir écrire dans les répertoires de configuration et de logs, le droit
en lecture peut être donné au groupe www, mais pas à others
- les répertoires et fichiers composant le serveur WWW doivent
pouvoir être accédés en lecture par l'utilisateur www.
Pour éviter de donner accès à des fichiers sans votre accord, vous pouvez enlever
le droit en lecture sur les répertoires, tout en laissant le droit en exécution (qui correspond
au droit d'entrer dans le répertoire).
Ainsi, un utilisateur pourra accéder à un fichier s'il en connaît le nom (cas d'une page html
faisant référence à une autre), mais il ne pourra visualiser la liste des fichiers contenus
dans un répertoire (ce qui se peut se produire lorsque on accède à
http://mon.site.domaine/repertoire/)
- si vous utilisez des liens pour donner plus de facilité à vos rédacteurs, faites en
sorte que ces liens pointent vers le serveur et non vers l'espace utilisateur. Cela vous permettra
de voir plus facilement tout ce qui se trouve dans votre serveur.
Exemple :
% ls -l /usr/local/www/
total 22
drwxr-xr-x 6 www www 1024 Oct 2 14:54 cgi-bin/
drwxr-x--x 2 www www 512 Sep 24 11:40 conf/
drwxr-x--x 4 www www 512 Oct 2 14:39 docs/
drwxr-x--- 10 www www 1024 Nov 4 00:00 logs/
% ls -ld /usr/local/www/docs
drwxr-x--x 3 redac1 reseau 512 Oct 7 13:38 atm/
drwxr-x--x 8 redac2 reseau 1536 Jul 19 08:22 ethernet/
drwxrwx--x 3 redac3 infos 512 Jul 26 09:45 infos/
drwxrwx--x 2 www cict 1024 Oct 31 11:48 icones/
drwxrwx--x 2 www cict 512 Jun 24 10:10 images/
lrwxrwxrwx 1 www www 14 Apr 3 1996 index.html -> infos/sommaire.html
% ls -ld ~redac1/www/atm
lrwxrwxrwx 1 redac1 reseau 27 Feb 22 1996 atm/ -> /usr/local/www/docs/atm
% ls -ld ~redac3/Documents/WWW/infos
lrwxrwxrwx 1 redac3 infos 27 Feb 22 1996 infos/ -> /usr/local/www/docs/infos
- si vous utilisez les restrictions d'accès aux pages par adresse IP ou par utilisateur et
mot de passe, centralisez tous les droits d'accès dans le fichier
access.conf plutôt que
de les distribuer dans les fichiers .htaccess disséminés dans la hiérarchie.
Un seul fichier permet de visualiser clairement la politique d'accès/de restriction de votre serveur
et a moins de chance d'être modifié par inadvertance.
- de même que vous avez supprimé les services non nécessaires sur votre serveur, n'activez pas
des fonctionnalités si vous n'en avez pas l'utilité dans l'immédiat, vous serez toujours à même de les
mettre en place ultérieurement.
Interrogez vous sur l'utilité des scripts présents dans
cgi-bin ainsi que des fonctionnalités
qui permettent :
- de lister le contenu d'un répertoire automatiquement (Options Indexes)
- de suivre les liens symboliques (Options FollowSymLinks). Mais ne pas positionner cette
option ne suffit pas pour qu'aucun lien ne soit suivi (le serveur teste seulement le dernier
élément dans le nom complet d'un fichier). Il faut de plus parcourir la hiérarchie de votre serveur
pour y rechercher les liens.
find /usr/local/www/doc -follow -type l -print affiche tous les liens situés dans le répertoire
/usr/local/www/doc et ses descendants.
- de réaliser des inclusions de fichier "à la volée" : Server Side Includes(Options IncludeNoExec)
Attention à la syntaxe du fichier acces.conf pour ne pas positionner ces options (Options None).
Vous écrivez des scripts CGI ...
Les scripts CGI en général
Les scripts CGI peuvent ouvrir deux types de brèches :
- ils peuvent laisser filtrer des informations sur votre système, permettant ainsi aux
pirates de mieux connaître votre système, leur facilitant ainsi la tâche.
- ils peuvent être trompés et exécuter à votre insu des commandes pour lesquelles ils
n'ont pas été écrits. Ainsi, un script peut envoyer le contenu de votre fichier des mots
de passe, et ce sans avoir besoin de privilèges particuliers.
Voici donc quelques trucs pour faire en sorte d'utiliser ces scripts dans des conditions plus sures,
classés par ordre d'importance :
- Ne jamais écrire un script CGI en shell (sh, csh, ksh, ...).
D'une part ce n'est pas la manière la plus
efficace de travailler (chaque ligne est interprétée), mais surtout le nombre de fois où les
caractères de contrôles peuvent être mal interprétés est beaucoup plus important.
- Ce qu'il ne faut pas faire dans un script CGI
- transmettre plus d'informations que nécessaire. Par exemple, si vous "enveloppez"
la commande finger, il n'est certainement pas nécessaire de diffuser le chemin d'accès au répertoire
principal de l'utilisateur ; il faut donc filtrer le résultat donné par finger.
- limiter à priori la taille des variables, ce qui peut entraîner des débordements ayant pour
conséquence un "plantage" du script et parfois l'exécution de commandes à votre insu.
- transmettre des paramètres à un interpréteur de commandes (shell) sans les avoir analysé.
Utiliser un interpréteur est réalisé :
- en C, avec system(), popen()
- en Perl, avec system(), exec(), open() combiné avec un |
- en shell avec exec, eval
- en utilisant les backquotes en Perl et shell
- Il est impératif de contrôler ce qui arrive en entrée de votre script.
Pour cela il est préférable de contrôler la conformité de cet entrée à ce qu'attend votre script que
de simplement ôter les caractères de contrôle. Par exemple, une adresse électronique SMTP est
généralement de la forme
nom.prenom@machine.domaine, les autres caractères
que @ . _ - a-z A-Z 0-9 n'ont donc rien à faire ici. (exemple)
En théorie, les caractères déclarés dans sendmail.cf sont les
suivants . : % @ ! ^ / [ ] a-z A-Z 0-9.
- La liste exhaustive des caractères de contrôle à filtrer avant de passer un paramètre à un
interpréteur est la suivante :
& ; ' ` \ " | * ? ~ < > ( ) [ ] { } $ \n \r
Voici deux exemples de contrôle sur les chaines de caractères, l'un
en C et l'autre en perl.
- Dans un script écrit en C, il est préférable d'utiliser la famille des fonctions exec() plutôt
que system(), car exec() ne passe pas par un interpréteur de commandes (shell).
- Il ne faut jamais faire confiance à la variable PATH pour lancer l'exécution
d'un programme mais toujours donner le chemin absolu pour l'atteindre.
En effet, une manoeuvre souvent utilisé pour corrompre un système est de modifier
la variable PATH afin de faire exécuter un programme modifié, du même nom, plutôt que celui que
vous attendez.
Pour la même raison, il est préférable d'éviter d'inclure le répertoire courant (.) dans
cette variable.
Par exemple, en C, préférez :
system("/bin/ls -l /usr/local/web/info");
à
system("ls -l /usr/local/web/info");
Et si vous devez utiliser la variable PATH, positionnez là complètement au début de votre script.
- Ne pas oublier que vos scripts peuvent être appelés sans passer par le formulaire que vous avez
écrit à cette intention. Des paramètres peuvent donc manquer ou contenir des valeurs inattendues.
Si vous définissez un champ <SELECT>, vérifiez avant d'utiliser la valeur de ce champ
qu'elle appartient à l'ensemble de valeurs présentées dans votre formulaire.
- Si vous avez l'alternative C/Perl ou plus généralement langage compilé/langage interprété
(même si Perl n'est pas qu'interprété mais aussi compilé), le fait
d'utiliser un langage compilé peut être intéressant pour les raisons suivantes :
- le code n'est pas fourni (le code d'un script peut être récupéré dans certains cas) et il est
moins volumineux
- on y utilise moins l'exécution de commandes systèmes et la capture de leur sortie
- Ne pas installer n'importe quel script récupéré sur le réseau sur votre serveur. Lisez-le et
analysez-le (ou faites-le analyser). Voici une check-list indicative :
- quelle est la complexité du script ? Plus il est complexe,
plus il y a de chances qu'il y ait un bug (cf le nombre de bugs découverts dans sendmail)
- Est ce qu'il lit/écrit dans des fichiers ?
- Est ce qu'il interagit avec d'autres programmes, notamment via un shell ?
- S'exécute-t-il suid (notamment suid root)?
- Vérifie-t-il ses entrées (caractères spéciaux) ?
- Utilise-t-il des chemins absolus ou fait-il confiance au PATH ?
- Rassembler tous les scripts CGI dans un même répertoire, tout simplement pour voir rapidement
quels sont les scripts utilisés par votre serveur. Ne définissez pas pour le serveur que
tout fichier se terminant par
.cgi est un script cgi (avec
AddType application/x-httpd-cgi .cgi), mais plutôt que tout fichier
situé dans tel et tel répertoire est un script cgi à exécuter comme tel (avec ScriptAlias).
Si de plus il n'est possible qu'au seul webmestre d'écrire dans le répertoire
cgi-bin, vous évitez l'installation d'un script buggé dans la hiérarchie
de votre serveur.
- Dans le répertoire
cgi-bin, ne laissez que les scripts qui sont utiles
(par défaut, enlevez les droits en exécution pour tous sur phf, finger, query, post-query, jj).
Pour en savoir plus :
Et les scripts Perl en particulier
- Eviter de passer par l'intermédiaire d'un shell pour appeler un programme.
Ainsi, préférez
system "/usr/bin/echo","$VARIABLE ";
à
system "/usr/bin/echo $VARIABLE ";
En effet, la variable $VARIABLE peut contenir des méta-caractères du shell.
Dans le premier cas, Perl ne passe pas par un shell pour exécuter /usr/bin/echo ;
les méta-caractères n'auront donc aucun effet.
- Utiliser les vérifications de compromission du Perl.
Ce mécanisme (taint checks) considère que toute variable provenant de l'extérieur du programme Perl
(variable d'environnement, valeur lue sur l'entrée standard ou provenant de la ligne de commande)
est compromise et ne peut par conséquent être utilisée pour modifier quoi que ce soit hors de
votre programme.
Ces variables ne peuvent donc pas être utilisées dans les appels aux fonctions eval(), system(),
exec() ; comme vous ne pouvez pas effacer un fichier dont le nom serait paramètré par une telle variable.
La compromission des variables se propage : si vous utilisez une telle variable V pour modifier la
valeur d'une autre variable U, U devient compromise.
Pour utiliser ce mécanisme, il faut utiliser un interpréteur particulier (taintperl) qui est appelé de
la manière suivante :
- en Perl v4 :
#!/usr/local/bin/taintperl
- en Perl v5 :
#!/usr/local/bin/perl -T
Et maintenant comment assainir ces variables ?
Le seul moyen d'assainir une variable considérée comme compromise est de rechercher une chaîne de
caractères puis d'extraire les sous-chaînes correspondantes :
$email = ~/([\w-.]+\@[\w-.]+)/;
$email_sain = $1;
- En Perl 5, vous pouvez utiliser les séquences \Q \E pour éviter que les méta-caractères soient
interprétés.
m/\Q$schema_recherche\E/;
Vous surfez sur le Web ...

Partie encore en cours d'élaboration
Que faire sur les butineurs ?
Sur les butineurs, attention à ne pas déclarer de "viewer externe" pour des fichiers pouvant contenir
des ordres exécutables. Il est en effet préférable de ne jamais exécuter à l'aveuglette n'importe quel
programme récupéré sur l'Internet.
Evitez par exemple de définir les viewers suivants :
| Document Type | Viewer |
| application/x-csh | /bin/csh |
| application/x-msexcel-macro | excel |
N'oubliez pas qu'un fichier Postscript peut contenir des ordres executables réalisant des actions
sur votre systèmes de fichiers.
Et java ?
Java est un langage développé par Sun MicroSystems. Les programmes Java sont pré-compilés
dans une forme compacte. Ils sont ensuite exécutés par une machine Java.
Certains programmes Java sont conçus pour etre exécutés dans un navigateur WWW : ce sont les
appliquettes (ou applets).
Comme java est basé sur une seule machine virtuelle que toutes les implémentations de java simulent,
les programmes java peuvent s'executer sur n'importe quel système disposant d'une version de java.
Les machines Java ont été conçues afin de minimiser les risques : ces machines ne peuvent exécuter
certaines commandes systèmes, ni charger des librairies systèmes ; elles ne peuvent pas ouvrir des fichiers
spéciaux correspondant à des disques, et l'écriture/la lecture de fichiers n'est possible que dans un
sous-ensemble de la hiérarchie.
Les document HTML peuvent faire référence à des applets grâce au marqueurs <APPLET>.
Les browsers qui comprennent ces marqueurs récupèrent
alors les applets sur le serveur WWW, puis les font exécuter à la machine Java du client.
Et javascript ?
JavaScript est un ensemble d'extensions apportées au langage HTML, supportées seulement par quelques
navigateurs (les versions 2.0 et supérieures de Netscape Navigator, la version 3 de Microsoft Internet
Explorer) afin de contrôler certains aspects du browser.
Que faire ?
Désactivez :
- Java (dans la boite de dialogue
Security Preferences)
- JavaScript (dans la boite de dialogue
Network&Security Options).
Pour rebondir ...
- Un article de Kelly Jackson Higgins (avril 96) sur la Sécurité des serveurs Web.
Mis à jour le 04/06/97
CICT
Vos commentaires sur ce serveur :
www@cict.fr