Stručně: Chcete-li Nginx pro PHP nastavit bezpečně, předávejte PHP-FPM jen skripty, které skutečně existují, u aplikací s front controllerem dokonce jen index.php. K tomu patří zablokované dotfiles jako .git a .env, výchozí server pro neznámé názvy hostitelů, vhodné limity velikosti a security.limit_extensions = .php v poolu. Před zranitelnostmi v samotném PHP to nechrání, ale omezuje to, co lze po chybě spustit.
Nejnebezpečnější řádky jsou v bloku location pro PHP
Od PHP2Shell kontroluje mnoho týmů své verze PHP-FPM a pooly, jak popisuje stránka Zabezpečení Nginx s PHP-FPM, a po aktualizaci restartuje všechny workery. Druhý pohled patří samotné konfiguraci Nginx. Tam se rozhoduje, které soubory se vůbec spouštějí jako PHP.
V mnoha konfiguracích stojí blok, který předává PHP-FPM každý požadavek na .php, bez ohledu na to, kde soubor leží. Pokud se vložený soubor PHP dostane do složky s nahranými soubory, server ho spustí. Ze souborové chyby se tak stanou zadní vrátka.
Tento článek se soustředí na stranu Nginx: které direktivy mají váhu, jak působí pořadí bloků location a jak výsledek otestovat.
Jak Nginx rozhoduje, který blok zabere
Kdo chce Nginx pro PHP nastavit bezpečně, musí znát výběr bloků location. Podle dokumentace Nginx probíhá takto:
- Přesná shoda s
location =hledání okamžitě ukončí. - Mezi prefixovými location se zapamatuje nejdelší odpovídající. Pokud nese
^~, regulární výrazy se už nekontrolují. - Jinak se regulární výrazy (
~,~*) kontrolují v pořadí konfiguračního souboru; vyhrává první shoda. - Pokud žádný regulární výraz nesedí, platí zapamatovaná prefixová location.
Z toho plynou dvě praktická pravidla. Zákaz PHP ve složkách s nahranými soubory musí být zapsán buď jako prefix ^~, nebo jako regulární výraz před obecným blokem PHP. A běžná prefixová location jako location /uploads/ nechrání, protože ji regulární výraz pro .php přebije.
Bezpečná základní konfigurace
Následující příklad ukazuje blok server pro aplikaci s front controllerem, jak je obvyklá u Laravelu, Symfony, Shopware 6 nebo mnoha vlastních vývojů. Cesty, socket a certifikáty si upravte.
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;
# Povolit Let's Encrypt a další známé cesty
location ^~ /.well-known/ {
try_files $uri =404;
}
# Zablokovat skryté soubory a složky (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# PHP-FPM se předává jen 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;
}
# Žádný jiný soubor PHP se nespouští
location ~ \.php$ {
return 404;
}
}
$realpath_root rozlišuje symbolické odkazy. To je důležité u nasazení se střídavým symlinkem na aktuální release, aby PHP-FPM a OPcache viděly správnou cestu. Mějte navíc na paměti: podle dokumentace Nginx se direktivy fastcgi_param dědí z vyšších úrovní jen tehdy, když na aktuální úrovni žádné vlastní nejsou. Proto patří include fastcgi_params; do téhož bloku.
Klasická CMS jako WordPress, TYPO3 nebo Contao mají několik vstupních bodů. Tam blok front controlleru nahraďte obecnou location pro PHP, která předává jen existující soubory (try_files $uri =404;), a zakažte spouštění ve všech zapisovatelných složkách. Jak na to, je v článku Zákaz spouštění PHP ve složkách s nahranými soubory.
Druhé zajištění v PHP-FPM
Nginx rozhoduje, co se předá, PHP-FPM rozhoduje, co spustí. Podle příručky PHP spouští PHP-FPM standardně soubory s příponou .php a .phar a sama doporučuje omezit na .php. Zachytí to chyby konfigurace ve webovém serveru.
; v poolu, např. /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
Socket by měl být dostupný jen pro Nginx. Porty FPM v síti, například 9000, se navenek otevírat nemají. Jak server celkově ukazuje jen nutné porty, popisuje Firewall pro webové servery: otevřít jen nutné porty.
Neznámé názvy hostitelů a další ladicí šrouby
Bez vlastního výchozího serveru odpovídá Nginx na požadavky na neznámé názvy hostitelů prvním odpovídajícím blokem server. Automatizované skeny, které oslovují jen adresu IP, pak míří přímo do vaší aplikace. Vlastní výchozí server takové požadavky ukončí bez odpovědi.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Další body, které se osvědčily:
| Ladicí šroub | Doporučení | Účel |
|---|---|---|
server_tokens |
off |
žádné číslo verze v hlavičce a na chybových stránkách |
client_max_body_size |
odpovídající post_max_size |
velké požadavky odmítat brzy |
autoindex |
vypnuto (výchozí) | žádné výpisy adresářů |
fastcgi_hide_header |
X-Powered-By |
žádná verze PHP navenek |
| Stavové stránky | pm.status_path jen interně |
žádné provozní údaje veřejně |
| Záložní soubory | .sql, .zip, .bak ve webrootu zablokovat nebo odstranit |
žádné stahování databází |
Více k údajům o verzi je v článku expose_php a hlavičky Server.
Další ladicí šroub se týká povolených metod HTTP. Většina webů potřebuje jen GET, HEAD a POST. Pomocí limit_except lze v location jiné metody odmítnout; podle dokumentace Nginx povolení GET automaticky povoluje i HEAD. Předem zkontrolujte, zda rozhraní vaší aplikace nepoužívají například PUT nebo DELETE, protože REST API to často dělají.
Otestovat před opětovným načtením
Každou změnu nejprve zkontrolujte pomocí nginx -t a teprve poté Nginx znovu načtěte. Poté otestujte chování zvenčí běžnými požadavky na vlastní doménu:
sudo nginx -t && sudo systemctl reload nginx
# Očekává se: 403 nebo 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
# Očekává se: 404, protože se spouští jen front controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Které bloky předávají PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Před každou větší změnou uložte úplný výstup nginx -T s datem. Obsahuje celou platnou konfiguraci včetně všech vložených souborů. Pokud se po změně něco pokazí, porovnání ihned ukáže, co se liší. Při další kritické zranitelnosti tak navíc máte spolehlivý podklad pro inventuru, aniž byste museli soubory teprve sbírat.
Konfiguraci měňte nejprve v testovacím prostředí nebo na kopii bloku server. Zapomenutý vstupní bod, například zpětné volání platební brány, se jinak ukáže až tehdy, když přestanou chodit objednávky.
Jak konkrétně pomáhá PHP SOS?
- Přečíst konfiguraci: Celou konfiguraci Nginx vyhodnotíme pomocí
nginx -Ta každé předání PHP přiřadíme jeho webu a poolu. - Určit vstupní body: Společně s vašimi vývojáři určíme, které skripty se musí spouštět, a zbytek zablokujeme.
- Zavést a otestovat: Upravíme bloky server a pooly, zkontrolujeme pomocí
nginx -ta otestujeme formuláře, platební cesty a nahrávání. - Zabezpečit trvale: Konfigurace se stane součástí našeho zpevnění serveru a dokumentace vašeho serveru.
Časté dotazy
Musí se Nginx po aktualizaci PHP restartovat?
Kvůli záplatě PHP ne, protože Nginx PHP neobsahuje. Restartovat se musí všechny služby PHP-FPM. Pokud při tom měníte i konfiguraci Nginx, zkontrolujte ji předem pomocí nginx -t a poté Nginx znovu načtěte.
Proč předávat PHP jen index.php?
Mnoho moderních aplikací má jediný vstupní bod, front controller. Pokud se PHP-FPM předává jen tento soubor, nelze vložený soubor PHP na jiném místě spustit přes prohlížeč. U systémů s mnoha vstupními body tento vzor možný není.
K čemu je security.limit_extensions v PHP-FPM?
Nastavení určuje, které přípony souborů PHP-FPM spouští jako skript. Podle příručky PHP je výchozí .php .phar; doporučuje se omezit na .php. Zachytí to chyby v konfiguraci webového serveru, ale nenahrazuje ji.
Jsou pravidla Nginx ochranou proti PHP2Shell?
Ne. PHP2Shell sídlí v interpretu PHP a uzavře se jen aktualizací na opravenou verzi a restartem všech workerů. Bezpečná konfigurace Nginx zmenšuje plochu pro útok a omezuje škody po průniku.