Krótko: Aby bezpiecznie skonfigurować Nginx dla PHP, przekazują Państwo do PHP-FPM tylko skrypty, które naprawdę istnieją, a w aplikacjach z front controllerem wyłącznie index.php. Do tego dochodzą zablokowane pliki z kropką, takie jak .git i .env, serwer domyślny dla nieznanych nazw hostów, odpowiednie limity rozmiaru i security.limit_extensions = .php w puli. Nie chroni to przed lukami w samym PHP, ale ogranicza to, co po błędzie można wykonać.
Najniebezpieczniejsze wiersze stoją w lokalizacji PHP
Od PHP2Shell wiele zespołów sprawdza swoje wersje PHP-FPM i pule, jak opisuje strona Zabezpieczanie Nginx z PHP-FPM, i po aktualizacji restartuje wszystkie procesy robocze. Drugie spojrzenie dotyczy samej konfiguracji Nginx. To tam rozstrzyga się, jakie pliki w ogóle są wykonywane jako PHP.
W wielu konfiguracjach stoi blok, który każde żądanie do .php przekazuje do PHP-FPM, bez względu na to, gdzie plik leży. Jeśli wstrzyknięty plik PHP trafi do katalogu uploadu, serwer go wykona. Z błędu dotyczącego pliku powstaje w ten sposób tylna furtka.
Ten artykuł koncentruje się na stronie Nginx: które dyrektywy się liczą, jak działa kolejność bloków location i jak przetestować wynik.
Jak Nginx decyduje, który blok zadziała
Kto chce bezpiecznie skonfigurować Nginx dla PHP, musi znać wybór bloków location. Według dokumentacji Nginx przebiega on tak:
- Dokładne trafienie z
location =natychmiast kończy wyszukiwanie. - Spośród lokalizacji z prefiksem zapamiętywana jest najdłuższa pasująca. Jeśli ma
^~, wyrażenia regularne nie są już sprawdzane. - W przeciwnym razie wyrażenia regularne (
~,~*) są sprawdzane w kolejności z pliku konfiguracyjnego; wygrywa pierwsze trafienie. - Jeśli żadne wyrażenie regularne nie pasuje, obowiązuje zapamiętana lokalizacja z prefiksem.
Wynikają z tego dwie praktyczne zasady. Blokada PHP w katalogach uploadu musi być założona albo jako prefiks ^~, albo jako wyrażenie regularne przed ogólnym blokiem PHP. A zwykła lokalizacja z prefiksem, taka jak location /uploads/, nie chroni, bo wyrażenie regularne dla .php ją przebija.
Bezpieczna konfiguracja podstawowa
Poniższy przykład pokazuje blok serwera dla aplikacji z front controllerem, jak w Laravel, Symfony, Shopware 6 lub wielu własnych rozwiązaniach. Ścieżki, gniazdo i certyfikaty należy dopasować.
server {
listen 443 ssl;
server_name www.example.de;
root /var/www/seite1/public;
index index.php;
ssl_certificate /etc/ssl/seite1/fullchain.pem;
ssl_certificate_key /etc/ssl/seite1/privkey.pem;
server_tokens off;
client_max_body_size 32m;
# Zezwolić na Let's Encrypt i inne znane ścieżki
location ^~ /.well-known/ {
try_files $uri =404;
}
# Zablokować ukryte pliki i katalogi (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Do PHP-FPM przekazywany jest tylko front controller
location = /index.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_hide_header X-Powered-By;
fastcgi_pass unix:/run/php/seite1.sock;
}
# Żaden inny plik PHP nie jest wykonywany
location ~ \.php$ {
return 404;
}
}
$realpath_root rozwiązuje dowiązania symboliczne. Jest to ważne przy wdrożeniach ze zmieniającym się dowiązaniem do bieżącego wydania, aby PHP-FPM i OPcache widziały właściwą ścieżkę. Proszę ponadto pamiętać: według dokumentacji Nginx dyrektywy fastcgi_param są dziedziczone z wyższych poziomów tylko wtedy, gdy na bieżącym poziomie nie ma własnych. Dlatego include fastcgi_params; należy do tego samego bloku.
Klasyczne systemy CMS, takie jak WordPress, TYPO3 czy Contao, mają kilka punktów wejścia. W ich przypadku blok front controllera zastępuje się ogólną lokalizacją PHP, która przekazuje tylko istniejące pliki (try_files $uri =404;), i blokuje wykonywanie we wszystkich zapisywalnych katalogach. Jak to zrobić, opisuje artykuł Zakazywanie wykonywania PHP w katalogach uploadu.
Drugie zabezpieczenie w PHP-FPM
Nginx decyduje, co zostanie przekazane, PHP-FPM decyduje, co wykona. Według podręcznika PHP PHP-FPM domyślnie wykonuje pliki z .php i .phar i sam zaleca ograniczenie do .php. Przechwytuje to błędy konfiguracji w serwerze WWW.
; w puli, np. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
Gniazdo powinno być dostępne tylko dla Nginx. Portów FPM w sieci, na przykład 9000, nie wolno otwierać na zewnątrz. Jak cały serwer pokazuje tylko niezbędne porty, opisuje artykuł Zapora dla serwera WWW: otwierać tylko niezbędne porty.
Nieznane nazwy hostów i dalsze pokrętła
Bez własnego serwera domyślnego Nginx odpowiada na żądania do nieznanych nazw hostów pierwszym pasującym blokiem serwera. Zautomatyzowane skany, które zwracają się tylko do adresu IP, trafiają wtedy prosto do Państwa aplikacji. Własny serwer domyślny kończy takie żądania bez odpowiedzi.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Dalsze sprawdzone punkty:
| Pokrętło | Zalecenie | Cel |
|---|---|---|
server_tokens |
off |
brak numeru wersji w nagłówku i na stronach błędów |
client_max_body_size |
dopasowane do post_max_size |
wcześnie odrzucać duże żądania |
autoindex |
wyłączone (domyślnie) | brak list katalogów |
fastcgi_hide_header |
X-Powered-By |
brak wersji PHP na zewnątrz |
| Strony statusu | pm.status_path tylko wewnętrznie |
brak danych eksploatacyjnych publicznie |
| Pliki kopii zapasowych | zablokować lub usunąć .sql, .zip, .bak w katalogu głównym |
brak pobierania baz danych |
Więcej o informacjach o wersji znajduje się w artykule expose_php i nagłówki Server.
Kolejne pokrętło dotyczy dozwolonych metod HTTP. Większość stron potrzebuje tylko GET, HEAD i POST. Za pomocą limit_except można w lokalizacji odrzucać inne metody; według dokumentacji Nginx zezwolenie na GET automatycznie zezwala też na HEAD. Proszę wcześniej sprawdzić, czy interfejsy Państwa aplikacji używają na przykład PUT lub DELETE, bo interfejsy REST często to robią.
Testowanie przed przeładowaniem
Każdą zmianę proszę najpierw sprawdzić poleceniem nginx -t i dopiero potem przeładować Nginx. Następnie testują Państwo zachowanie z zewnątrz zwykłymi żądaniami do własnej domeny:
sudo nginx -t && sudo systemctl reload nginx
# Oczekiwane: 403 lub 404
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/HEAD
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
# Oczekiwane: 404, ponieważ wykonywany jest tylko front controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Które bloki przekazują do PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Przed każdą większą zmianą proszę zabezpieczyć pełne wyjście nginx -T z datą. Zawiera ono całą obowiązującą konfigurację łącznie ze wszystkimi dołączonymi plikami. Jeśli po zmianie coś pójdzie źle, porównanie od razu pokazuje, co się różni. Przy następnej krytycznej luce mają Państwo ponadto wiarygodną podstawę do inwentaryzacji, bez konieczności zbierania plików.
Konfigurację proszę zmieniać najpierw w środowisku testowym lub na kopii bloku serwera. Zapomniany punkt wejścia, na przykład callback płatności, wychodzi inaczej na jaw dopiero wtedy, gdy brakuje zamówień.
Jak konkretnie pomaga PHP SOS?
- Odczyt konfiguracji: analizujemy całą konfigurację Nginx za pomocą
nginx -Ti przyporządkowujemy każde przekazanie do PHP jego stronie i puli. - Ustalenie punktów wejścia: wspólnie z Państwa programistami określamy, które skrypty muszą być wykonywane, i blokujemy resztę.
- Wdrożenie i test: dostosowujemy bloki serwera i pule, sprawdzamy za pomocą
nginx -toraz testujemy formularze, drogi płatności i uploady. - Trwałe zabezpieczenie: konfiguracja staje się częścią naszego utwardzania serwerów i dokumentacji Państwa serwera.
Częste pytania
Czy Nginx trzeba zrestartować po aktualizacji PHP?
Dla poprawki PHP nie, ponieważ Nginx nie zawiera PHP. Restartu wymagają wszystkie usługi PHP-FPM. Jeśli przy tym zmieniają Państwo także konfigurację Nginx, proszę najpierw sprawdzić ją poleceniem nginx -t, a potem przeładować Nginx.
Dlaczego przekazywać do PHP tylko index.php?
Wiele nowoczesnych aplikacji ma jeden punkt wejścia, front controller. Jeśli do PHP-FPM przekazywany jest tylko ten plik, wstrzyknięty plik PHP w innym miejscu nie może zostać wykonany przez przeglądarkę. W systemach z wieloma punktami wejścia taki wzorzec nie jest możliwy.
Co daje security.limit_extensions w PHP-FPM?
Ustawienie określa, jakie rozszerzenia plików PHP-FPM wykonuje jako skrypt. Według podręcznika PHP domyślnie jest to .php .phar; zaleca się ograniczenie do .php. Przechwytuje to błędy w konfiguracji serwera WWW, ale jej nie zastępuje.
Czy reguły Nginx chronią przed PHP2Shell?
Nie. PHP2Shell tkwi w interpreterze PHP i zamyka się go wyłącznie aktualizacją do naprawionej wersji oraz restartem wszystkich procesów roboczych. Bezpieczna konfiguracja Nginx zmniejsza powierzchnię ataku i ogranicza szkody po włamaniu.