Serveur web mutualisé (5/5) : Des pistes de sécurisation
Sommaire
I. Présentation
Dans les précédents billets, nous avons donc vu ce qu'était un serveur web mutualisé, de quoi il était constitué et pourquoi sa structure globale pouvait être facilement déjouée pour outre-passer les limitations standards mises en place. Dans le dernier billet de ce dossier, nous allons étudier des pistes pour sécuriser les vulnérabilités aperçues précédemment. Loin de moi l'idée de proposer une solution 100% sécurisée qui révolutionnerait le marché, je vise plutôt dans ce billet à donner des débuts d'idées et des pistes à parcourir pour arriver à résoudre certains problèmes rencontrés.
II. Récapitulatif
Il convient tout d'abord de poser sur la table les principaux points qui posent problèmes dans notre contexte :
- Un serveur web mutualisé héberge par définition plusieurs sites qui tournent donc tous sur le même utilisateur
- De ce fait, l'exécution d'un script menant des actions sur le serveur via cet utilisateur par les clients pose un problème de sécurité car ces derniers peuvent profiter des droits de l'utilisateur web pour se promener sur les serveurs
Techniquement cela se résume en trois points :
- Les utilisateurs peuvent voir, via des scripts PHP, le contenu des autres utilisateurs web du serveur
- Les utilisateurs peuvent voir, via des scripts PHP, tous les fichiers word readable et l'arborescence du serveur
- Des fonctions PHP trop permissives existent, sont disponibles sur PHP par défaut et ne sont pas forcément utilisées par les clients standards.
Globalement, il faut donc restreindre le champ d'action des scripts PHP de nos utilisateurs web. Etant donné que nous ne sommes pas maitre du code qu'ils constituent, nous pouvons agir sur ce sur quoi nous avons la main : les services qui exécutent ce code.
Après avoir cherché de nombreuses façons de se protéger de cet ensemble de problèmes, j'ai établi la mise en place de trois actions qui permettent de sécuriser un peu plus le fonctionnement d'un serveur web mutualisé. Nous allons ici voir en quoi ces outils peuvent nous aider ainsi que leur fonctionnement respectif, leur mise en place dans un environnement simulant un serveur web mutualisé (voir tutoriel numéro 3 sur l'architecture basique d'un serveur web mutualisé).
III. Apache ITK
Apache-mpm-ikt est une version d'apache qui se base sur la version traditionnelle d'Apache (apache-mpm-prefork), soit une version multi-processus mais non-threaded, à l'inverse d'apache-mpm-worker. La subtilité supplémentaire qu'apporte cette version d'Apache est qu'elle permet de faire tourner chaque vHost sous un uid:gid différent en précisant ceux-ci dans la configuration de chaque vHost. Plus clairement, nous essayons ici de contrer le fait que tous les vHost doivent tourner sous l'utilisateur apache (qui est www-data sous Debian, apache sous CentOS), ce qui est gênant puisqu'en prenant le rôle d'apache/www-data via des script PHP qu'apache exécute, les utilisateurs pouvaient se balader sur le serveur. Il faut donc ici remplacer la version apache-mpm-prefork (standard) de son serveur par apache-mpm-itk.
apt-get install apache-mpm-itk
Une fois la réinstallation faite, on pourra vérifier que nous sommes bien en version ITK via la commande suivante :
apache2 -V
Pour Debian et :
httpd -V
pour CentOS.
une fois cette vérification faite, il nous faudra aller préciser pour chaque vHost sous quel utilisateur il doit tourner. Sans indication contraire, ceux-ci continueront de tourner sous www-data, ce qui peut être un choix pour certains d'entre eux. Logiquement, on mettra en UID:GID ceux de l'utilisateur possédant un compte FTP sur le vHost en question (logique). Dans chaque configuration de vHost on précisera donc
<IfModule mpm_itk_module> AssignedUserId uid:gid </IfModule>
Bien entendu, on remplacera "uid" et "gid" par les uid et gid respectifs de l'utilisateur possédant les droits sur le répertoire web visé. Idéalement il faut donc faire ça sur tous nos vHost et leur dédier un utilisateur chacun. On pourra alors redémarrer le service web Apache pour que ces changements de configurations soient pris en compte :
service apache2 restart
On pourra ensuite faire un petit test (comme ceux fait lors du tutoriel précédent sur l'explication et l’exploitation des vulnérabilités trouvées). On créera donc un script "shell.php" contenant un code qui ira lire le contenu du répertoire voisin (c'est sur ce point précis que le module itk intervient), nous nous occuperons des autres problèmes par la suite :
<?php $C = file_get_contents ('/storage/web/www-blog2/config.php'); echo $C; ?>
On voit donc que nous n'arrivons plus à charger et à lire les fichiers d'un espace web depuis un autre. De plus, cela nous permet de ne plus avoir de fichiers "world readable", c'est à dire avec les droits r-x pour les "others". On peut donc repasser les droits des fichiers du répertoire web en 770 (au minimum).
Au passage, nous pourrons en profiter pour restreindre les droits que nous mettons par défaut aux fichiers uploadés par le FTP. Pour une configuration VSFTPD, on mettra la valeur de "local_umask" à "027" pour que seul l'utilisateur propriétaire puisse écrire et que les "others" ne puissent ni lire, ni écrire. On redémarrera notre serveur avec la commande "service vsftpd restart"
IV. Php OpenBase Dir
Cependant, même si les utilisateurs ne peuvent plus ouvrir de fichier d'un répertoire web à un autre, il peuvent toujours lire les fichiers qu'on appelle "Word readable" hors répertoires web avec des fonctions comme "fopen" qui permettent de lire et d'écrire dans de tel fichier (selon permission bien sûr). Le meilleur exemple de fichier word readable sous Linux étant bien entendu /etc/passwd qui contient la liste des utilisateurs du système. Nous allons donc utiliser pour se protéger cela du paramètre "open_basedir" de PHP. Ce paramètre permet de préciser un chemin au dessus duquel aucun fichier ne pourra être visé (ouvert, lu ou modifié) par un script PHP.
Plus clairement si l'open_basedir est /dir1/dir2 et qu'un fichier PHP vise un fichier se trouvant à l'extérieur de ce dossier, il se fera bloquer par le paramétrage open_basedir.
D'accord, mais si on précise un dossier web d'un client comme /storage/web/www-blog1, les autres dossiers ne pourront plus exécuter de script PHP du tout, même dans leur propre répertoire ?
Oui précisément, c'est pour cela que nous n'allons pas indiquer ce paramétrage dans le fichier de configuration principal de PHP (à savoir php.ini) mais à nouveau dans la configuration de chaque vHost, ce qui nous donne la possibilité de définir un dossier open_basedir bien précis et différent pour chaque vHost.
Dans chaque fichier de configuration vHost d'Apache, on précisera donc le paramètre suivant (aprés la première valise "<Directory>") où on remplacement la valeur "none" si la ligne existe déjà :
php_admin_value "/storage/..répertoire_du_vhost../"
Il faut donc adapter chaque chemin pour chaque configuration de vHost. On redémarre ensuite notre service Apache puis on refait un test depuis notre accès utilisateur en uploadant puis en chargeant le script suivant qui va afficher le contenu du fichier /etc/passwd grâce à la fonction get_file_content :
<?php $C = file_get_contents ('/etc/passwd'); echo $C; ?>
Et on voit que nous n'avons plus accès au contenu d'/etc/passwd
V. PHP Disable Function
Pour les curieux qui auraient déjà testé, on peut voir que si on utilise la fonction que j'ai utilisé lors du précédent tutoriel (à savoir shell_exec, exec ou encore passthru, escapeshellcmd ou system), on arrive toujours à lire le contenu des fichiers word readable. Cela est du que la restriction que nous avons mise plus haut sont des restrictions au niveau PHP et que les fonctions que je viens de citer "descendent" un niveau en dessous car elles font exécuter des commandes directement sur le système, nous ne sommes donc plus du tout dans le cadre d'exécution d'un script PHP, malgré les apparences.
Ces fonctions, bien définies, sont des fonctions qui peuvent être utilisées par des sites web, des CMS, bien que ce soit rarement le cas. La seule façon de les bloquer et ainsi de faire en sorte que les utilisateurs ne puissent plus se déplacer dans le serveur est donc de les interdire au niveau de la configuration générale de PHP via le paramètre disable_functions. Ainsi, elles ne pourront plus du tout être exécutées via des scripts PHP. On se rend pour cela dans le fichier de configuration général de PHP
/etc/php5/apache2/php.ini
Sous Debian ou
/etc/php.ini
sous Centos puis on cherche le paramètre disable_functions qui contient normalement déjà des fonctions, on y rajoute ensuite les commandes suivantes :
On pourra alors redémarrer une dernière fois apache2 :
service apache2 restart
Sous Debian ou
service httpd restart
sous CentOS
Puis vérifier notre protection en exécutant à nouveau notre dernier script :
<?php $C = shell_exec(cat /etc/passwd'); echo $C; ?>
et on voit donc que rien ne s'affiche à l'écran.
VI. Il reste des risques !
Je suis bien conscient que les solutions que je propose sont loin d'être parfaites, elles ont sûrement d'autres failles qui sont toutefois je pense plus dure à exploiter. Sinon elles seraient déjà largement plus diffusées. Cependant je cherche via cet article à lancer et stimuler le débat afin d'avoir un échange d'idées sur comment pourrait on sécuriser un tel espace ? Si vous avez de meilleurs solutions de sécurité ou que vous savez comment déjouer les procédures de sécurité que je viens d'exposer, n'hésitez pas à me l'annoncer, elles n'en deviendrons que plus sûres !
Il est également important de signaler que la mise en place de ces outils va, par leur simple présence et fonctionnement, ralentir (de façon non catastrophique) le traitement des requêtes PHP du serveur. Cela est dû au simple fait que les différentes requêtes et exécutions sont un peu plus surveillées qu'avant. Même avec des solutions totalement libres, la sécurité a un prix ...
A vos claviers !
Les autres articles :
Hello,
Bravo pour cette suite de 5 billets, un sacré boulot et c’est fort intéressant. Mes remarques :
– Je trouve particulièrement mal formulé l’écriture du billet comme ceci
apache2 -V
Pour Debian et :
httpd -V
pour CentOS.
– Pourquoi les billets 1 et 2 de ce « dossier » ne font pas partis de http://www.it-connect.fr/tutoriels/serveur-web/apache/ ?
– Par rapport au point précédent, il serait bienvenue de mettre des liens vers les billets précédents car il est par exemple compliqué de retrouver les deux premiers billets
– Je viens de faire une recherche avec le moteur du site « serveur web mutualisé » retourne les 5 billets, « serveur web mutualisés » n’en retourne que 4
– Par rapport au point précédent, il faudrait supprimer le s de mutualisés pour ce 5ème billet
Tcho !
Bonjour Cascador,
Merci pour tes remarques, je modifierais quelques passage en en prenant compte =)
Les billets 1 et 2 ne sont pas dans apache car ce sont des billets moins techniques qui ne sont pas forcément en rapport qu’avec Apache mais ils relève plus de la théorie (dans le sens où ce n’est pas de la pratique) à l’inverse des trois derniers billets qui eux sont purement technique.
Je mettrais des liens sur chacun des billets vers les autres pour favoriser et simplifier la navigation, merci de ta remarque =)
Pour Les recherches Google, j’ai effectivement rajouter un S en trop que je viens d’enlever, mais au niveau de ce qui est affiché ou pas en fonction des requêtes, je n’ai pas trop de pouvoir en la matière, même si j’aimerais ! =)
Encore merci et à bientôt
Qu’en est il de la solution (certes plus lourde) des machines virtuelles , en terme de sécurité ?