PHP lit sa configuration depuis
/etc/php5/apache2/php.ini
. Par defaut, un certain
nombre de paramètres sont plutôt dangereux et méritent d'être revus.
La première et la plus radicale des restictions possibles est de
désactiver l'interprétation des scripts PHP globalement
sur le serveur avec la directive engine off
. On pourra
les autoriser ensuite dans les VirtualHosts par des
directives spéciales comme décrit dans Section 5.2.4, « Exceptions ».
Même si ce n'est pas un moyen de protection valable, on peut toujours
masquer la présence de PHP afin de rendre les choses plus difficiles pour les
éventuels attaquants. PHP est plutôt verbeux sur sa présence. On trouve même
dans les versions récentes, des URL
easter-eggs
[10]. On pourra les supprimer
en mettant la variable expose_php
[11] à
Off
. Cela ne suffit bien sûr pas : quand on a
affaire à des fichiers se terminant par .php
,
cela lève le
doute assez rapidement... Il faudra donc aussi modifier le
handler PHP, définit dans
/etc/apache2/mods-available/php5.conf
pour y ajouter
d'autres extensions moins « bavardes ». On pourra même demaner à Apache
de renvoyer tous les fichiers .html
au moteur PHP. C'est
discret, mais le coût en performance peut être important si la proportion
pages statiques/pages dynamiques est défavorable sur le serveur.
Lorsque PHP est activé (globalement, ou sur un
virtualhost), la dernière chose que nous voulons le voir
faire est de charger du code à notre insu. Il est donc conseillé de mettre
la valeur de enable_dl
à
Off
.
allow_url_fopen
permet de charger du code PHP
distant en ouvrant un URL (par include ou
fopen). Il faudra aussi désactiver cette possibilité en
mettant la directive à Off
.
PHP a toujous été handicapé par la
nécessité de maintenir une certaine compatibilité
ascendante. Les variables register_globals
,
register_long_arrays
et
register_argc_argv
font partie de cet héritage.
Mais pour déployer des scripts « modernes » et surtout bien écrits,
il n'est pas nécessaire (et même dangereux) d'activer ces paramètres. Il faudra
donc tous les mettre à Off
.
PHP sait faire beaucoup. Parfois un peut trop dans le contexte de scripts
web. Il est heureusement possible de desactiver certaines fonctions du language
avec disable_functions
. Il est probablement
plus sûr de désactiver les fonctions suivantes :
chgrp
,
chmod
,
chown
,
diskfreespace
,
dl
,
escapeshellcmd
,
exec
,
get_current_user
,
getmypid
,
getmyuid
,
getrusage
,
highlight_file
,
ignore_user_abord
,
ini_alter
,
ini_restore
,
ini_set
,
leak
,
link
,
listen
,
login
,
passthru
,
php_uname
,
popen
,
posix_mkfifo
,
posix_setegid
,
posix_seteuid
,
posix_setgid
,
posix_setpgid
,
posix_setsid
,
posix_setuid
,
proc_get_status
,
proc_nice
,
proc_open
,
proc_terminate
,
set_time_limit
,
shell_exec
,
show_source
,
socket_bind
,
socket_listen
,
system
et
virtual
.
Attention toutefois : on ne peut
configurer ce paramètre que dans php.ini
, et non dans la
configuration d'Apache (principale ou virtualhost). La
valeur affectée sera donc globale.
Sur des serveurs ayant plusieurs virtualhosts, il
est préférable de restreindre l'accès au filesystem aux
secteurs strictement
nécessaires. L'utilisateur alice qui met ses pages,
par exemple, dans /home/alice/www/
n'a pas besoin d'ouvrir
des fichiers se trouvant au dessus de son répertoire web dans
le filesystem. Le paramètre open_basedir
permet de configurer un répertoire au dessus duquel il ne sera pas possible
d'ouvrir de fichier. Il conviendra de le spécifier dans chaque
virtualhost.
doc_root
permet de spécifier la racine
des scripts php, à ne pas confondre avec
open_basedir
qui donne la
racine de tous les fichiers accessibles par PHP (éventuellement des
fichiers de données ou des fichiers de session).
Pour des raisons de sécurité, on veillera aussi
à déplacer le répertoire utilisé pour stocker les fichiers de session (par
défaut, /var/lib/php5/
sous Ubuntu). Il faudra créér un
répertoire propre à chaque virtualhost, afin de limiter les éventuels
problèmes de vol de session. On utilisera
la variable session.save_path
pour spécifier
le nouveau répertoire et on veillera à mettre une valeur située
sous celle
d'open_basedir
. Dans le cas contraire, PHP ne
pourrait sauver les fichiers de session.
Selon la destination du serveur, on pourra être tenté de positionner la
variable file_uploads
à
off
. Cela désactivera la possibilité d'envoyer
des fichiers en POST vers des scripts PHP.
Si l'on désire conserver cette possibilité, il vaut mieux modifier la
variable upload_tmp_dir
et lui affecter un
répertoire accessible depuis le virtualhost (donc sous le répertoire
pointé par open_basedir
).
Un paramètre regroupe à lui seul plusieurs restrictions améliorant
la sécurité : safe_mode
. Il permet
notamment de faire respecter les droits associés aux fichiers (un fichier
appartenant à X
ne pourra être utilisé par un script appartenant à
Y
) et
d'empécher la modification des variables d'environnement listées dans le
paramètre safe_mode_protected_env_vars
.
Le paramètre safe_mode
désactive aussi
certaintes fonctions,
listées
dans le manuel de PHP, donc la plupart sont déja couvertes par la valeur
recommandée pour disable_functions
. Il faudra
donc mettre cette valeur à On
sans
hésiter.
PHP permet de fixer quelques limites pour l'exécution des scripts. Ces limites pemettent d'éviter d'avoir un serveur complètement surchargé par l'exécution d'un seul script. En revanche, il n'y a pas de provision possible sur le nombre de scripts exécutables en parallèle, qui potentiellement peut être égal au nombre de processus Apache. Il faut donc garder à l'esprit que ce n'est pas un mécanisme qui permet de se protéger contre les attaques, mais plutôt contre des erreurs de programmations.
memory_limit
permet de limiter la
mémoire maximum utilisable par un script PHP. Il est vivement recommandé de ne
pas laisser la valeur pas défaut de 128M.
Il est difficile d'estimer la bonne valeur pour un script, mais dans la plupart
des cas, 10M
devraient amplement suffire.
La durée d'exécution d'un script peut être fixée à l'aide de
max_execution_time
. Par défaut, sa valeur
est 30 (secondes).
max_input_time
permet de limiter la
durée d'une requète client. Par défaut, il n'y a aucune valeur maximum. Des
clients mal configurés pourraient donc mobiliser des scripts trop longtemps. En
positionnant cette variable à une valeur différente de -1, on peut limiter ce
temps. Il faut cependant noter que ce temps comprend la totalité de la
requète cliente, y compris des fichiers « uploadés » en POST. Donc
si le script recoit des fichiers importants (ou sert à faire des tests de
bande passante par exemple), il faudra prévoir un valeur suffisamment grande
pour les clients lents.
max_input_time
est donc liée à
la variable upload_max_filesize
qui limite la
taille maximale d'un fichier uploadé (2M par défaut).
De même, la taille maximum d'un envoi par POST, fixé dans
post_max_size
(8M par défaut), devra être
supérieur à upload_max_filesize
(pour couvrir
l'envoi de fichier) et servira à calibrer la durée maximum de la requète
client.
Pour clore la série des limitations, on pourra aussi en citer une
qu'il est peut être souhaitable d'appliquer à la configuration Apache.
Les scripts PHP incluent souvent des portions de code depuis d'autres fichiers,
se terminant en général par .inc
. Bien que rien n'oblige
un développeur à nommer ces fichiers .inc, c'est une
pratique assez courante[12]. Le problème avec
cette pratique est qu'Apache, par défaut, n'a pas de
handler (gestionnaire) associé aux extensions
.inc
. Un fichier avec une telle extension fichier sera
donc affiché comme n'importe quel
autre fichier texte, donnant des informations potentiellement sensibles
[13].
On peut demander à Apache de ne pas servir ces fichiers : ils sont inclus
directement depuis les scripts coté serveur; inutile donc d'offrir la
possibilité aux clients HTTP de voir le contenu de ces fichiers. Il en va de
même pour les fichiers avec les extensions ~
,
.bak
, .swp
,
.old
, .sav[e]
, etc... qui sont
parfois envoyés en FTP en même temps que des fichiers légitimes ou laissés
sur place par des développeurs éditant directement sur le serveur.
<Files ~ "\.(inc|bak|swp|old|sav[e]?|~)$"> Order allow,deny Deny from all </Files>
PHP a un système de gestion d'erreurs qui lui permet de les afficher
dans les pages HTML retournées ou de les envoyer vers un fichier spécifique.
Il vaudra mieux utiliser cette dernière méthode pour les applications en
production : cela permet de donner un minimum d'informations via la page Web
tout en permettant aux développeurs de voir les messages d'erreurs. Si l'on
désire masquer la présence de PHP c'est aussi une modification recommandée.
log_errors
à
On
permet d'envoyer les erreurs vers le
fichier définit dans error_log
. On
désactivera ensuite les messages d'erreur avec
display_errors
et
display_startup_errors
à
Off
.
Dans le cas ou un paramètre doit avoir une valeur spéciale pour un
VirutalHost particulier, il est possible d'affecter des
valeurs aux variables de configuration PHP dans une section
<VirtualHost ...>
avec la directive
php_admin_value
. Par exemple, si le
VirutalHost d'Alice doit pouvoir utiliser l'upload de
fichiers et nécessite 50 Mb de mémoire pour son exécution, on pourra corriger
la valeur de file_uploads
et
memory_limit
uniquement pour lui :
root@ubuntu:~# cat /etc/apache2/sites-available/site_alice
<VirtualHost *>
ServerAdmin alice@exemple.org
ServerName alice.exemple.org
DocumentRoot /home/alice/monsiteouebe/
php_admin_flag engine on
php_admin_flag file_uploads on
php_admin_value memory_limit 50M
...
On privilégiera systématiquement php_admin_value
au lieu de php_value
pour changer les valeurs de
configuration de PHP dans la configuration Apache. Cette dernière directive
autorise le changement des valeurs dans un fichier
.htaccess
tandis que php_admin_value
la fixe sans aucune possibilité de modification.
Seules les valeurs expose_php
,
disable_functions
et
disable_classes
ne peuvent pas être
modifiées dans la configuration d'Apache (voir
http://fr.php.net/manual/fr/ini.php).
[10] Par exemple celui-ci donnant affichant les différents crédits concernant le développement de PHP.
[11] cette variable n'est utilisable que dans
php.ini
.
[12] Cette pratique pourrait être
avantageusement remplacée par une autre consistant à nommer ces fichiers en
.php
et à les placer dans un sous répertoire
/include
par exemple.
[13] On pourra s'en convaincre en visitant, par exemple, cette adresse http://www.google.com/search?q=filetype%3Ainc+inurl%3Aconfig+%22dbname%22