L'option PHP 'cgi.fix_pathinfo' est-elle vraiment dangereuse avec Nginx + PHP-FPM?

51

Il y a eu un grand nombre de parler d'un problème de sécurité par rapport à l' cgi.fix_pathinfooption PHP utilisé avec Nginx (habituellement PHP-FPM, CGI rapide).

En conséquence, le fichier de configuration par défaut de nginx disait:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

Cependant, le wiki "officiel" de Nginx stipule que PATH_INFO peut être géré correctement sans désactiver l’option PHP ci-dessus. Et alors?

Des questions

  • Pouvez-vous expliquer clairement ce que cgi.fix_pathinfofait? (La documentation officielle indique simplement : "Pour plus d'informations sur PATH_INFO, voir les spécifications CGI")
  • Qu'est - ce que PHP est vraiment avec ces PATH_INFOet les SCRIPT_FILENAMEvariables?
  • Pourquoi et comment cela peut-il être dangereux avec Nginx? ( exemples détaillés )
  • Le problème existe-t-il toujours dans les versions récentes de ces programmes?
  • Apache est-il vulnérable?

J'essaie de comprendre le problème à chaque étape. Par exemple, je ne comprends pas pourquoi l'utilisation du socket Unix php-fpm pourrait éviter ce problème.

Totor
la source
1
Vous pouvez répondre à votre propre question en comprenant la différence entre PATH_INFO et PATH_TRANSLATED: blogs.msdn.com/b/david.wang/archive/2005/08/04/…
Giovanni Tirloni

Réponses:

79

TL; DR - le correctif (dont vous n’avez peut-être même pas besoin) est TRÈS SIMPLE et se trouve à la fin de cette réponse.

Je vais essayer de répondre à vos questions spécifiques, mais votre incompréhension de PATH_INFO rend les questions elles-mêmes un peu fausses.

  • La première question devrait être "Quelle est cette entreprise de chemin d’information?"

  • Votre prochaine question aurait dû être: "Comment PHP détermine-t- PATH_INFOil quoi et qui SCRIPT_FILENAMEsont?"

    • Les versions précédentes de PHP étaient naïves et, techniquement, elles ne supportaient même pas PATH_INFO. Par conséquent, ce qui était supposé être PATH_INFOétait connu , ce qui est effectivement SCRIPT_FILENAMEcassé dans de nombreux cas. Je ne dispose pas d’une version suffisamment ancienne de PHP pour effectuer des tests, mais j’ai SCRIPT_FILENAMEpensé qu’elle se présentait comme suit: "/path/to/script.php/THIS/IS/PATH/INFO" dans l’exemple ci-dessus (préfixé par la docroot comme d’habitude).
    • Avec cgi.fix_pathinfo activé, PHP trouve maintenant correctement « / CE / IS / PATH / INFO » pour l'exemple ci - dessus et la met en PATH_INFOet SCRIPT_FILENAMEobtient juste la partie qui pointe vers le script étant demandé (préfixé avec le docroot bien sûr).
    • Remarque: lorsque PHP a pris en charge la prise en charge PATH_INFO, il a fallu ajouter un paramètre de configuration pour la nouvelle fonctionnalité afin que les personnes utilisant des scripts dépendant de l'ancien comportement puissent exécuter les nouvelles versions de PHP. C'est pourquoi il existe même un commutateur de configuration pour cela. Il aurait dû être intégré (avec le comportement "dangereux") depuis le début.
  • Mais comment PHP sait-il quelle partie est le script et quelle est son information de chemin? Et si l'URI est quelque chose comme:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • Cela peut être une question complexe dans certains environnements. Qu'est-ce qui se passe en PHP, c'est qu'il trouve la première partie du chemin de l'URI qui ne correspond à rien sous la docroot du serveur. Pour cet exemple, il voit que sur votre serveur, vous n'avez pas "/docroot/path/to/script.php/THIS" mais vous avez très certainement "/docroot/path/to/script.php" SCRIPT_FILENAMEa été déterminé et PATH_INFOobtient le reste.
    • Alors maintenant, le bon exemple du danger qui est bien détaillé dans les documents Nginx et dans la réponse de Hrvoje Špoljar (vous ne pouvez pas être agité quant à un exemple aussi clair) devient encore plus clair: avec l'exemple de Hrvoje (" exemple: http: //. com / foo.jpg / nonexistent.php "), PHP voit un fichier sur votre docroot" /foo.jpg "mais il ne voit rien qui s'appelle" /foo.jpg/nonexistent.php ", alors il SCRIPT_FILENAMEobtient" /foo.jpg " (encore une fois, préfixé par docroot) et PATH_INFOobtient "/nonexistent.php".
  • Pourquoi et comment cela peut être dangereux devrait maintenant être clair:

    • Le serveur Web n’est vraiment pas en cause, c’est simplement un proxy pour l’URI de PHP, qui trouve innocemment que "foo.jpg" contient en réalité du contenu PHP, il l’exécute donc (vous avez maintenant été pwned!). Ce n'est pas particulier à Nginx en soi.
  • Le REAL problème est que vous laissez le contenu non sécurisé être téléchargé quelque part sans désinfectante et vous permettre à d' autres demandes arbitraires au même endroit, où PHP exécute heureusement quand il peut.
  • Nginx et Apache pourraient être construits ou configurés pour empêcher les requêtes utilisant cette astuce, et il existe de nombreux exemples sur la façon de procéder, y compris dans la réponse de user2372674 . Cet article de blog explique bien le problème, mais il manque la bonne solution.

  • Cependant, la meilleure solution consiste simplement à s'assurer que PHP-FPM est configuré correctement pour qu'il ne puisse jamais exécuter un fichier s'il ne se termine pas par ".php". Il est à noter que les versions récentes de PHP-FPM (~ 5.3.9 +?) Ont ceci par défaut, ce danger n’est donc plus un problème.

La solution

Si vous avez une version récente de PHP-FPM (~ 5.3.9 +?), Vous ne devez rien faire car le comportement sécurisé ci-dessous est déjà le comportement par défaut.

Sinon, trouvez le www.conffichier php-fpm (cela /etc/php-fpm.d/www.confdépend de votre système). Assurez-vous d'avoir ceci:

security.limit_extensions = .php

Encore une fois, c'est le défaut dans de nombreux endroits ces jours-ci.

Notez que cela n'empêche pas un attaquant de télécharger un fichier ".php" dans un dossier de téléchargement WordPress et de l'exécuter avec la même technique. Vous devez toujours avoir une bonne sécurité pour vos applications.

utilisateur109322
la source
5
Bonne réponse! Pour clarifier: si, comme vous le dites, PHP détermine ce qui SCRIPT_FILENAMEest, pourquoi y a-t-il une fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;ligne dans ma nginxconf? Est-ce que cela annule les efforts de PHP pour découvrir la valeur de SCRIPT_FILENAMElui-même?
Totor
Y at-il une fonction pour obtenir la valeur de security.limit_extensions? J'ai essayé phpinfo(), ini_get(security.limit_extensions)et ini_get_all()sans succès.
elbowlobstercowstand
Merci, si les versions récentes de PHP-FPM (~ 5.3.9 +?) L’ont par défaut, pourquoi php7.1 en a besoin? Ou est- ce que cet article est faux?
Yevgeniy Afanasyev
14

Essentiellement, sans cela, vous pouvez télécharger un fichier avec un code php nommé "foo.jpg" sur le serveur Web; puis demandez-le comme http: //domain.tld/foo.jpg/nonexistent.php et la pile de serveurs Web dira à tort oh; c'est un PHP; J'ai besoin de traiter cela, il ne parviendra pas à trouver foo.jpg / nonexistent.php, donc il retombera sur foo.jpg et traiter foo.jpg en tant que code php. C'est dangereux car cela ouvre le système à une intrusion très facile; toute application Web permettant par exemple le téléchargement d’images devient un outil de téléchargement de porte dérobée.

En ce qui concerne l'utilisation de php-fpm avec un socket pour l'éviter; OMI cela ne résoudra pas le problème.

Hrvoje Špoljar
la source
Vous ne répétez que ce qui peut être lu sur les liens que j'ai fournis. Vous n'expliquez pas le mécanisme réel. Votre réponse a besoin de valeur ajoutée à mon humble avis.
Totor
6
C'est peut-être vrai, mais votre titre a une question et la réponse à cette question est dans ma réponse. Si vous le voulez explicitement; oui c'est dangereux; très dangereux.
Hrvoje Špoljar
1 / Ma réponse ne se limite pas à son titre: elle a un corps. 2 / user109322 vous a prouvé le contraire : quelle que soit la valeur utilisée, elle cgi.fix_pathinfon’est pas dangereuse, car le php-fpmfichier conf par défaut est sûr (il n’exécutera que les fichiers portant l’ .phpextension).
Totor
2

Dans le wiki de Nginx en tant que mesure de sécurité

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

est inclus dans le bloc emplacement. Dans d'autres tutoriels

try_files $uri =404;

est utilisé, ce qui devrait faire la même chose, mais peut donner des problèmes selon le wiki de Nginx. Avec ces options, cgi.fix_pathinfo=1ne devrait plus être un problème. Plus d'informations peuvent être trouvées ici .

utilisateur2372674
la source