Czy opcja PHP „cgi.fix_pathinfo” jest naprawdę niebezpieczna w przypadku Nginx + PHP-FPM?

51

Nastąpił wiele od mówienia o występowanie problemu dotyczącego zabezpieczeń w stosunku do możliwości używanego PHP z Nginx (zwykle PHP-FPM, szybko CGI). cgi.fix_pathinfo

W rezultacie domyślny plik konfiguracyjny nginx powiedział:

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

Jednak teraz „oficjalna” wiki Nginx stwierdza, że PATH_INFO może być poprawnie obsługiwane bez wyłączania powyższej opcji PHP. Więc co?

pytania

  • Czy możesz wyjaśnić jasno, co robi cgi.fix_pathinfo? ( oficjalny dokument mówi po prostu : „Aby uzyskać więcej informacji na temat PATH_INFO, zobacz specyfikacje CGI”)
  • Co PHP naprawdę zrobi z tymi PATH_INFOi SCRIPT_FILENAMEzmiennymi?
  • Dlaczego i jak może być niebezpiecznie z Nginx? ( szczegółowe przykłady)
  • Czy problem nadal występuje w najnowszych wersjach tych programów?
  • Czy Apache jest zagrożony?

Staram się zrozumieć problem na każdym kroku. Na przykład nie rozumiem, dlaczego użycie uniksowego gniazda php-fpm może uniknąć tego problemu.

Totor
źródło
1
Możesz odpowiedzieć na własne pytanie, rozumiejąc różnicę między PATH_INFO a PATH_TRANSLATED: blogs.msdn.com/b/david.wang/archive/2005/08/04/…
Giovanni Tirloni

Odpowiedzi:

79

TL; DR - poprawka (której możesz nawet nie potrzebować) jest BARDZO PROSTA i na końcu tej odpowiedzi.

Spróbuję odpowiedzieć na twoje konkretne pytania, ale twoje niezrozumienie, czym jest PATH_INFO, sprawia, że ​​same pytania są trochę błędne.

  • Pierwsze pytanie powinno brzmieć: „Co to za biznes informacji o ścieżce?”

  • Twoje następne pytanie powinno brzmieć: „W jaki sposób PHP określa, co PATH_INFOi czym SCRIPT_FILENAMEjest?”

    • Wcześniejsze wersje PHP były naiwne i technicznie nawet nie wspierały PATH_INFO, więc to, co miało być, PATH_INFOzostało zmutowane, na SCRIPT_FILENAMEktórym tak jest w wielu przypadkach zepsute. Nie mam wystarczająco starej wersji PHP do testowania, ale uważam, że widziałem ją SCRIPT_FILENAMEjako cały shebang: „/path/to/script.php/THIS/IS/PATH/INFO” w powyższym przykładzie (z prefiksem docroot jak zwykle).
    • Po włączeniu cgi.fix_pathinfo PHP poprawnie znajduje teraz „/ THIS / IS / PATH / INFO” dla powyższego przykładu i umieszcza go PATH_INFOi SCRIPT_FILENAMEpobiera tylko część wskazującą na żądany skrypt (oczywiście poprzedzony plikiem docroot).
    • Uwaga: kiedy PHP zaczęło faktycznie obsługiwać PATH_INFO, musieli dodać ustawienie konfiguracyjne dla nowej funkcji, aby osoby uruchamiające skrypty zależne od starego zachowania mogły uruchamiać nowe wersje PHP. Dlatego jest nawet przełącznik konfiguracji. Powinien był być wbudowany (z „niebezpiecznym” zachowaniem) od samego początku.
  • Ale skąd PHP wie, jaką częścią jest skrypt i jakie informacje o ścieżce? Co jeśli URI jest podobny do:

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

    • To może być złożone pytanie w niektórych środowiskach. To, co dzieje się w PHP, polega na tym, że znajduje pierwszą część ścieżki URI, która nie odpowiada żadnemu elementowi w pliku docroot serwera. W tym przykładzie widać, że na twoim serwerze nie masz „/docroot/path/to/script.php/THIS”, ale na pewno masz „/docroot/path/to/script.php”, więc teraz SCRIPT_FILENAMEzostał ustalony i PATH_INFOdostaje resztę.
    • Więc teraz dobry przykład niebezpieczeństwa, który jest szczegółowo opisany w dokumentach Nginx i odpowiedzi Hrvoje Špoljara (nie możesz być wybredny na temat tak wyraźnego przykładu), staje się jeszcze bardziej wyraźny: biorąc pod uwagę przykład Hrvoje'a („ http: // przykład. com / foo.jpg / nonexistent.php ”), PHP widzi plik w twoim pliku docroot„ /foo.jpg ”, ale nie widzi nic o nazwie„ /foo.jpg/nonexistent.php ”, więc SCRIPT_FILENAMEdostaje„ /foo.jpg ” (ponownie z prefiksem docroot) i PATH_INFOotrzymuje „/nonexistent.php”.
  • Dlaczego i jak może być niebezpieczne, powinno być teraz jasne:

    • Serwer sieciowy naprawdę nie ponosi winy - to jedynie przybliżenie URI do PHP, co niewinnie stwierdza, że ​​„foo.jpg” faktycznie zawiera treść PHP, więc wykonuje ją (teraz jesteś pwned!). To NIE szczególności Nginx per se.
  • NIERUCHOMOŚCI problemem jest to, że pozwalasz niezaufane treść przesłania gdzieś bez odkażania i pozwalasz innych dowolnych żądań do tej samej lokalizacji, co PHP szczęśliwie wykonuje kiedy mogę.
  • Nginx i Apache mogą zostać zbudowane lub skonfigurowane, aby zapobiegać żądaniom przy użyciu tej sztuczki, i istnieje wiele przykładów, jak to zrobić, w tym w odpowiedzi user2372674 . Ten artykuł na blogu ładnie wyjaśnia problem, ale brakuje w nim właściwego rozwiązania.

  • Jednak najlepszym rozwiązaniem jest upewnienie się, że PHP-FPM jest poprawnie skonfigurowany, aby nigdy nie wykonywał pliku, dopóki nie zakończy się na „.php”. Warto zauważyć, że najnowsze wersje PHP-FPM (~ 5.3.9 +?) Mają to ustawienie domyślne, więc to niebezpieczeństwo nie stanowi już większego problemu.

Rozwiązanie

Jeśli masz najnowszą wersję PHP-FPM (~ 5.3.9 +?), Nie musisz nic robić, ponieważ poniższe zachowanie jest już domyślne.

W przeciwnym razie znajdź www.confplik php-fpm (może to /etc/php-fpm.d/www.confzależeć od twojego systemu). Upewnij się, że masz to:

security.limit_extensions = .php

Ponownie, w dzisiejszych czasach jest to domyślne.

Pamiętaj, że nie uniemożliwia to atakującemu przesłania pliku „.php” do folderu przesyłania WordPress i wykonania go przy użyciu tej samej techniki. Nadal potrzebujesz dobrego bezpieczeństwa dla swoich aplikacji.

użytkownik109322
źródło
5
Niezła odpowiedź! Wyjaśnij: jeśli, jak mówisz, PHP określa, co to SCRIPT_FILENAMEjest, dlaczego fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;w moim nginxconf jest linia ? Czy zastępuje wysiłki PHP mające na celu odkrycie wartości SCRIPT_FILENAMEsamej w sobie?
Totor,
Czy istnieje funkcja pozwalająca uzyskać wartość security.limit_extensions? Próbowałem phpinfo(), ini_get(security.limit_extensions)i ini_get_all()bez powodzenia.
elbowlobstercowstand
Dziękujemy, jeśli najnowsze wersje PHP-FPM (~ 5.3.9 +?) Mają to ustawienie domyślne, dlaczego php7.1 tego potrzebuje? Czy ten artykuł jest błędny?
Jewgienij Afanasiew
14

W zasadzie bez tego możesz przesłać plik z kodem php o nazwie „foo.jpg” na serwer; następnie poproś o to tak, jak http: //domain.tld/foo.jpg/nonexistent.php, a stos serwera sieciowego omyłkowo powie: och; to jest PHP; Muszę to przetworzyć, nie uda się znaleźć foo.jpg / nonexistent.php, więc wróci do foo.jpg i przetworzy foo.jpg jako kod php. Jest to niebezpieczne, ponieważ otwiera system na bardzo łatwą ingerencję; każda aplikacja internetowa umożliwiająca na przykład przesyłanie obrazu staje się narzędziem do przesyłania backdoora.

Odnośnie używania php-fpm z gniazdem uniksowym, aby tego uniknąć; IMO nie rozwiąże problemu.

Hrvoje Špoljar
źródło
Powtarzasz tylko to, co można przeczytać na podanych przeze mnie linkach. Nie wyjaśniasz prawdziwego mechanizmu. Twoja odpowiedź wymaga wartości dodanej IMHO.
Totor
6
To może być prawda, ale twój tytuł ma pytanie, a odpowiedź na to pytanie jest w mojej odpowiedzi. Jeśli chcesz tego wyraźnie; tak, to jest niebezpieczne; bardzo niebezpieczne.
Hrvoje Špoljar
1 / Moja odpowiedź nie ogranicza się do tytułu: ma ciało. 2 / user109322 udowodnił, że się mylisz: dowolna użyta wartość niecgi.fix_pathinfo jest niebezpieczna, ponieważ domyślna konf jest bezpieczna (będzie wykonywać tylko pliki z rozszerzeniem). php-fpm.php
Totor,
2

Na wiki Nginx jako środek bezpieczeństwa

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

jest zawarty w bloku lokalizacji. W innych samouczkach

try_files $uri =404;

jest używany, co powinno zrobić to samo, ale może powodować problemy zgodnie z wiki Nginx. Dzięki tym opcjom cgi.fix_pathinfo=1nie powinno już stanowić problemu. Więcej informacji można znaleźć tutaj .

użytkownik2372674
źródło