Ukratko: Da biste Nginx za PHP konfigurirali sigurno, PHP-FPM-u predajte samo skripte koje doista postoje, a kod aplikacija s front controllerom čak samo index.php. Tome pripadaju blokirani dotfileovi poput .git i .env, zadani poslužitelj za nepoznate nazive hostova, prikladne granice veličine i security.limit_extensions = .php u poolu. To ne štiti od ranjivosti u samom PHP-u, ali ograničava što se nakon pogreške može izvršiti.
Najopasniji reci nalaze se u PHP lokaciji
Od PHP2Shella mnogi timovi provjeravaju svoje verzije PHP-FPM-a i poolove, kako opisuje stranica Zaštititi Nginx s PHP-FPM-om, i nakon ažuriranja ponovno pokreću sve workere. Drugi pogled vrijedi samoj Nginx konfiguraciji. Tamo se odlučuje koje se datoteke uopće izvršavaju kao PHP.
U mnogim konfiguracijama postoji blok koji svaki zahtjev za .php prosljeđuje PHP-FPM-u, svejedno gdje se datoteka nalazi. Ako ubačena PHP datoteka završi u mapi za prijenose, poslužitelj je izvršava. Iz pogreške s datotekom tako nastaju stražnja vrata.
Ovaj se članak usredotočuje na Nginx stranu: koje direktive računaju, kako djeluje redoslijed location blokova i kako testirati rezultat.
Kako Nginx odlučuje koji blok djeluje
Tko želi sigurno konfigurirati Nginx za PHP, mora poznavati odabir location blokova. Prema dokumentaciji Nginxa on teče ovako:
- Točan pogodak s
location =odmah završava pretragu. - Među prefiksnim lokacijama pamti se najdulja koja odgovara. Ako nosi
^~, regularni izrazi se više ne provjeravaju. - Inače se regularni izrazi (
~,~*) provjeravaju redoslijedom iz konfiguracijske datoteke; prvi pogodak pobjeđuje. - Ako nijedan regularni izraz ne odgovara, vrijedi zapamćena prefiksna lokacija.
Iz toga proizlaze dva praktična pravila. Blokada PHP-a u mapama za prijenose mora se postaviti ili kao ^~ prefiks ili kao regularni izraz ispred općeg PHP bloka. A obična prefiksna lokacija poput location /uploads/ ne štiti jer je regularni izraz za .php nadjačava.
Sigurna osnovna konfiguracija
Sljedeći primjer prikazuje server blok za aplikaciju s front controllerom, kakav je uobičajen kod Laravela, Symfonyja, Shopwarea 6 ili mnogih vlastitih razvoja. Putove, socket i certifikate prilagodite.
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;
# Dopustiti Let's Encrypt i druge poznate putove
location ^~ /.well-known/ {
try_files $uri =404;
}
# Blokirati skrivene datoteke i mape (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Samo se front controller predaje PHP-FPM-u
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;
}
# Nijedna druga PHP datoteka se ne izvršava
location ~ \.php$ {
return 404;
}
}
$realpath_root razrješuje simboličke poveznice. To je važno kod deploymenta s promjenjivom simboličkom poveznicom na aktualno izdanje, kako bi PHP-FPM i OPcache vidjeli ispravan put. Imajte na umu i ovo: prema dokumentaciji Nginxa direktive fastcgi_param s viših razina nasljeđuju se samo ako na trenutnoj razini nema vlastitih. Zato include fastcgi_params; pripada u isti blok.
Klasični CMS-ovi poput WordPressa, TYPO3-a ili Contaoa imaju više ulaznih točaka. Tamo blok front controllera zamijenite općom PHP lokacijom koja predaje samo postojeće datoteke (try_files $uri =404;) i blokirajte izvršavanje u svim zapisivim mapama. Kako to uspijeva, piše u članku Zabraniti izvršavanje PHP-a u mapama za prijenose.
Druga zaštita u PHP-FPM-u
Nginx odlučuje što se prosljeđuje, a PHP-FPM što izvršava. Prema PHP priručniku PHP-FPM zadano izvršava datoteke s .php i .phar i sam preporučuje da se to ograniči na .php. To hvata konfiguracijske pogreške u web-poslužitelju.
; u poolu, npr. /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 bi trebao biti dostupan samo Nginxu. FPM portovi u mreži, primjerice 9000, ne smiju se otvarati prema van. Kako poslužitelj u cjelini pokazuje samo nužne portove, opisuje članak Vatrozid za web-poslužitelje: otvoriti samo nužne portove.
Nepoznati nazivi hostova i druge sklopke
Bez vlastitog zadanog poslužitelja Nginx zahtjeve za nepoznate nazive hostova odgovara prvim odgovarajućim server blokom. Automatizirana skeniranja koja se obraćaju samo IP adresi tada završavaju izravno u Vašoj aplikaciji. Zaseban zadani poslužitelj takve zahtjeve završava bez odgovora.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Daljnje točke koje su se pokazale dobrima:
| Sklopka | Preporuka | Svrha |
|---|---|---|
server_tokens |
off |
bez broja verzije u zaglavljima i na stranicama s pogreškama |
client_max_body_size |
prilagođeno post_max_size |
rano odbiti velike zahtjeve |
autoindex |
isključeno (zadano) | bez popisa direktorija |
fastcgi_hide_header |
X-Powered-By |
bez verzije PHP-a prema van |
| Status stranice | pm.status_path samo interno |
bez javnih podataka o radu |
| Datoteke sigurnosnih kopija | blokirati ili ukloniti .sql, .zip, .bak u web-korijenu |
bez preuzimanja baza podataka |
Više o podacima o verziji nalazi se u članku expose_php i Server zaglavlja.
Još jedna sklopka odnosi se na dopuštene HTTP metode. Većini web-stranica trebaju samo GET, HEAD i POST. Pomoću limit_except druge se metode u lokaciji mogu odbiti; prema dokumentaciji Nginxa odobrenje GET-a automatski dopušta i HEAD. Prethodno provjerite koriste li sučelja Vaše aplikacije primjerice PUT ili DELETE, jer REST API-ji to često čine.
Testirati prije ponovnog učitavanja
Svaku izmjenu najprije provjerite naredbom nginx -t, a tek zatim ponovno učitajte Nginx. Nakon toga ponašanje testirajte izvana običnim zahtjevima na vlastitu domenu:
sudo nginx -t && sudo systemctl reload nginx
# Očekuje se: 403 ili 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čekuje se: 404, jer se izvršava samo front controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Koji blokovi predaju PHP-FPM-u?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Prije svake veće izmjene osigurajte potpuni ispis nginx -T s datumom. Sadrži cijelu djelotvornu konfiguraciju uključujući sve uključene datoteke. Ako nakon izmjene nešto pođe po zlu, usporedba odmah pokazuje što se razlikuje. Kod sljedeće kritične ranjivosti imate uz to pouzdanu osnovu za inventuru, bez potrebe da prvo skupljate datoteke.
Konfiguraciju najprije mijenjajte na testnom okruženju ili kopiji server bloka. Zaboravljena ulazna točka, primjerice povratni poziv plaćanja, inače se primijeti tek kad narudžbe izostanu.
Kako PHP SOS konkretno pomaže?
- Pročitati konfiguraciju: cijelu Nginx konfiguraciju analiziramo pomoću
nginx -Ti svaku predaju PHP-u pridružujemo njezinoj web-stranici i poolu. - Odrediti ulazne točke: zajedno s Vašim programerima određujemo koje se skripte moraju izvršavati, a ostalo blokiramo.
- Provesti i testirati: prilagođavamo server blokove i poolove, provjeravamo naredbom
nginx -ti testiramo obrasce, putove plaćanja i prijenose. - Trajno osigurati: konfiguracija postaje dio našeg ojačavanja poslužitelja i dokumentacije Vašeg poslužitelja.
Česta pitanja
Treba li Nginx ponovno pokrenuti nakon PHP ažuriranja?
Za PHP zakrpu ne, jer Nginx ne sadrži PHP. Ponovno se moraju pokrenuti sve PHP-FPM usluge. Ako pritom mijenjate i Nginx konfiguraciju, prethodno je provjerite naredbom nginx -t, a zatim ponovno učitajte Nginx.
Zašto PHP-u predavati samo index.php?
Mnoge moderne aplikacije imaju jednu jedinu ulaznu točku, front controller. Ako se PHP-FPM-u predaje samo ta datoteka, ubačena PHP datoteka na drugom mjestu ne može se izvršiti preko preglednika. Kod sustava s mnogo ulaznih točaka taj obrazac nije moguć.
Što donosi security.limit_extensions u PHP-FPM-u?
Postavka određuje koje ekstenzije datoteka PHP-FPM izvršava kao skriptu. Prema PHP priručniku zadano je .php .phar; preporučuje se ograničenje na .php. To hvata pogreške u konfiguraciji web-poslužitelja, ali je ne zamjenjuje.
Jesu li Nginx pravila zaštita od PHP2Shella?
Nisu. PHP2Shell je u PHP interpreteru i zatvara se samo ažuriranjem na ispravljenu verziju i ponovnim pokretanjem svih workera. Sigurna Nginx konfiguracija smanjuje napadnu površinu i ograničava štetu nakon provale.