Nginx

PHP 選項“cgi.fix_pathinfo”對於 Nginx + PHP-FPM 真的很危險嗎?

  • January 21, 2020

關於與Nginx 使用的 PHP 選項(通常是 PHP-FPM,快速 CGI)相關的安全問題已經 很多 討論。 cgi.fix_pathinfo

結果,預設的nginx配置文件習慣說:

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

但是,現在,“官方”Nginx wiki聲明PATH_INFO 可以在不禁用上述 PHP 選項的情況下正確處理。所以呢?

問題

  • 你能清楚地解釋一下是做什麼的cgi.fix_pathinfo嗎?(官方文件只是說:“有關 PATH_INFO 的更多資訊,請參閱 CGI 規範”)
  • PHP 將如何處理這些PATH_INFOSCRIPT_FILENAME變數?
  • Nginx 為什麼以及如何危險?(詳細例子)
  • 這些程序的最新版本中是否仍然存在該問題?
  • Apache易受攻擊嗎?

我試圖了解每一步的問題。例如,我不明白為什麼使用 php-fpm Unix 套接字可以避免這個問題。

TL; DR - 修復(您甚至可能不需要)非常簡單,並且在此答案的末尾。

我將嘗試解決您的具體問題,但是您對 PATH_INFO 是什麼的誤解使問題本身有點錯誤。

  • 第一個問題應該是“這個路徑資訊業務是什麼?”

    • 路徑資訊是 URI 中腳本之後的內容(應以正斜杠開頭,但在以 a 開頭的查詢參數之前結束?)。維基百科關於 CGI的文章概述部分的最後一段很好地總結了它。下面PATH_INFO是“/THIS/IS/PATH/INFO”:

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

  • 您的下一個問題應該是:“PHP 如何確定什麼PATH_INFOSCRIPT_FILENAME是什麼?”

    • 早期版本的 PHP 很幼稚,從技術上講甚至不支持PATH_INFO.,所以應該是PATH_INFOSCRIPT_FILENAME,是的,在很多情況下都被破壞了。我沒有足夠舊的 PHP 版本來測試,但我相信它SCRIPT_FILENAME在上面的範例中看到了整個 shebang:“/path/to/script.php/THIS/IS/PATH/INFO”(前綴為docroot 像往常一樣)。
    • 啟用 cgi.fix_pathinfo 後,PHP 現在可以正確找到上述範例的“/THIS/IS/PATH/INFO”,並將其放入PATH_INFOSCRIPT_FILENAME獲取指向所請求腳本的部分(當然,以 docroot 為前綴)。
    • 注意:當 PHP 開始真正支持PATH_INFO時,他們必須為新功能添加配置設置,以便執行依賴於舊行為的腳本的人可以執行新的 PHP 版本。這就是為什麼它甚至有一個配置開關。它應該從一開始就內置(具有“危險”行為)。
  • 但是 PHP 怎麼知道腳本的哪一部分以及它的路徑資訊呢?如果 URI 類似於:

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

  • 在某些環境中,這可能是一個複雜的問題。在 PHP 中發生的情況是,它會找到與伺服器 docroot 下的任何內容都不對應的 URI 路徑的第一部分。對於這個例子,它發現在你的伺服器上你沒有“/docroot/path/to/script.php/THIS”但是你肯定有“/docroot/path/to/script.php”所以現在SCRIPT_FILENAME已經下定決心,PATH_INFO得到剩下的。
  • 因此,現在 Nginx 文件和Hrvoje Špoljar 的回答(你不能對這樣一個清晰的例子過於挑剔)中詳細描述的危險的好例子變得更加清晰:鑑於 Hrvoje 的例子(“ http://example. com/foo.jpg/nonexistent.php “),PHP 在你的 docroot “/foo.jpg” 上看到一個文件,但它沒有看到任何名為 “/foo.jpg/nonexistent.php” 的文件,所以SCRIPT_FILENAME得到 “/foo.jpg” (同樣,以 docroot 為前綴)並PATH_INFO獲得“/nonexistent.php”。
  • 現在應該清楚它為什麼以及如何造成危險:

    • Web 伺服器確實沒有錯 - 它只是將 URI 代理到 PHP,它無辜地發現“foo.jpg”實際上包含 PHP 內容,所以它執行它(現在你已經被 pwned 了!)。這並不是Nginx 本身所特有的。
  • 真正的問題是你讓不受信任的內容上傳到某個地方而不進行清理,並且你允許其他任意請求到同一位置,PHP 會在可能的情況下愉快地執行。

  • 可以建構或配置 Nginx 和 Apache 以防止使用此技巧的請求,並且有很多範例說明如何做到這一點,包括在user2372674 的答案中。這篇部落格文章很好地解釋了這個問題,但它缺少正確的解決方案。

  • 但是,最好的解決方案是確保 PHP-FPM 配置正確,這樣它就永遠不會執行文件,除非它以“.php”結尾。值得注意的是,最新版本的 PHP-FPM (~5.3.9+?) 將其作為預設設置,因此這種危險不再是太大的問題。

解決方案

如果您有最新版本的 PHP-FPM(~5.3.9+?),那麼您無需執行任何操作,因為下面的安全行為已經是預設設置。

否則,找到 php-fpm 的www.conf文件(也許/etc/php-fpm.d/www.conf,取決於你的系統)。確保你有這個:

security.limit_extensions = .php

同樣,這在當今許多地方都是預設設置。

請注意,這並不能阻止攻擊者將“.php”文件上傳到 WordPress 上傳文件夾並使用相同的技術執行該文件。您仍然需要為您的應用程序提供良好的安全性。

引用自:https://serverfault.com/questions/627903