Trumpai: norėdami saugiai sukonfigūruoti Nginx PHP, į PHP-FPM perduokite tik scenarijus, kurie tikrai egzistuoja, Front-Controller programoms net tik index.php. Prie to priklauso užblokuoti dotfiles, tokie kaip .git ir .env, numatytasis serveris nežinomiems pagrindinio kompiuterio vardams (hostname), tinkamos dydžio ribos ir security.limit_extensions = .php baseine. Tai neapsaugo nuo spragų pačiame PHP, bet riboja, kas po klaidos gali būti vykdoma.
Pavojingiausios eilutės yra PHP location
Po PHP2Shell daugelis komandų tikrina savo PHP-FPM versijas ir baseinus, kaip aprašo puslapis Nginx su PHP-FPM apsauga, ir po atnaujinimo iš naujo paleidžia visus darbinius procesus (worker). Antras žvilgsnis skiriamas pačiai Nginx konfigūracijai. Būtent ten nusprendžiama, kurie failai apskritai vykdomi kaip PHP.
Daugelyje konfigūracijų yra blokas, kuris kiekvieną užklausą .php perduoda PHP-FPM, nesvarbu, kur failas yra. Jei įterptas PHP failas patenka į įkėlimų aplanką, serveris jį vykdo. Iš failo klaidos taip atsiranda užpakalinės durys.
Šis straipsnis koncentruojasi į Nginx pusę: kurios direktyvos svarbios, kaip veikia location blokų eilės tvarka ir kaip patikrinti rezultatą.
Kaip Nginx nusprendžia, kuris blokas taikomas
Kas nori saugiai sukonfigūruoti Nginx PHP, turi žinoti location blokų pasirinkimą. Pasak Nginx dokumentacijos, jis vyksta taip:
- Tikslus atitikmuo su
location =iš karto baigia paiešką. - Tarp prefiksinių location įsimenama ilgiausia tinkama. Jei ji turi
^~, reguliarieji reiškiniai nebetikrinami. - Kitu atveju reguliarieji reiškiniai (
~,~*) tikrinami konfigūracijos failo eilės tvarka; laimi pirmas atitikmuo. - Jei joks reguliarusis reiškinys netinka, taikoma įsimintoji prefiksinė location.
Iš to išplaukia dvi praktinės taisyklės. PHP blokavimas įkėlimų aplankuose turi būti sukurtas arba kaip ^~ prefiksas, arba kaip reguliarusis reiškinys prieš bendrą PHP bloką. O įprasta prefiksinė location, pavyzdžiui, location /uploads/, neapsaugo, nes .php reguliarusis reiškinys ją nustelbia.
Saugi bazinė konfigūracija
Toliau pateiktas pavyzdys rodo serverio bloką programai su Front-Controller, kaip įprasta Laravel, Symfony, Shopware 6 ar daugelyje savo kūrimo programų. Kelius, lizdą ir sertifikatus pritaikykite.
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;
# Leisti Let's Encrypt ir kitus žinomus kelius
location ^~ /.well-known/ {
try_files $uri =404;
}
# Užblokuoti paslėptus failus ir aplankus (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Tik Front-Controller perduodamas PHP-FPM
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;
}
# Joks kitas PHP failas nevykdomas
location ~ \.php$ {
return 404;
}
}
$realpath_root išsprendžia simbolines nuorodas. Tai svarbu diegiant su besikeičiančia simboline nuoroda į dabartinį leidimą (release), kad PHP-FPM ir OPcache matytų teisingą kelią. Taip pat atkreipkite dėmesį: pasak Nginx dokumentacijos, fastcgi_param direktyvos iš aukštesnių lygių paveldimos tik tada, kai dabartiniame lygyje savų nėra. Todėl include fastcgi_params; priklauso tam pačiam blokui.
Klasikiniai CMS, tokie kaip WordPress, TYPO3 ar Contao, turi kelis įėjimo taškus. Ten Front-Controller bloką pakeiskite bendra PHP location, kuri perduoda tik egzistuojančius failus (try_files $uri =404;), ir užblokuokite vykdymą visuose įrašomuose aplankuose. Kaip tai pasiekti, aprašyta skyriuje PHP vykdymo įkėlimų aplankuose draudimas.
Antra apsauga PHP-FPM
Nginx nusprendžia, kas perduodama, PHP-FPM nusprendžia, ką vykdo. Pasak PHP vadovo, PHP-FPM numatytai vykdo failus su .php ir .phar ir pats rekomenduoja tai apriboti iki .php. Tai pagauna žiniatinklio serverio konfigūracijos klaidas.
; baseine, pvz., /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
Lizdas turėtų būti pasiekiamas tik Nginx. FPM prievadai tinkle, pavyzdžiui, 9000, neturi būti atveriami į išorę. Kaip serveris apskritai rodo tik reikalingus prievadus, aprašo Ugniasienė žiniatinklio serveriui: atverti tik reikalingus prievadus.
Nežinomi hostname ir kiti reguliavimo varžtai
Be savo numatytojo serverio Nginx užklausas nežinomiems hostname atsako pirmuoju tinkamu serverio bloku. Automatizuoti skenavimai, kurie kreipiasi tik į IP adresą, tada patenka tiesiai į jūsų programą. Atskiras numatytasis serveris tokias užklausas nutraukia be atsakymo.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Kiti pasiteisinę punktai:
| Reguliavimo varžtas | Rekomendacija | Paskirtis |
|---|---|---|
server_tokens |
off |
jokio versijos numerio antraštėje ir klaidų puslapiuose |
client_max_body_size |
pagal post_max_size |
dideles užklausas atmesti anksti |
autoindex |
išjungtas (numatytasis) | jokių katalogų sąrašų |
fastcgi_hide_header |
X-Powered-By |
PHP versijos neišduoti į išorę |
| Būsenos puslapiai | pm.status_path tik viduje |
jokių eksploatacijos duomenų viešai |
| Atsarginių kopijų failai | .sql, .zip, .bak žiniatinklio šakniniame kataloge užblokuoti arba pašalinti |
jokio duomenų bazių atsisiuntimo |
Daugiau apie versijų duomenis rasite straipsnyje expose_php ir Server antraštė.
Kitas reguliavimo varžtas liečia leidžiamus HTTP metodus. Daugumai svetainių reikia tik GET, HEAD ir POST. Su limit_except kitus metodus galima atmesti location bloke; pasak Nginx dokumentacijos, GET leidimas automatiškai leidžia ir HEAD. Iš anksto patikrinkite, ar jūsų programos sąsajos nenaudoja, pavyzdžiui, PUT ar DELETE, nes REST API tai daro dažnai.
Išbandykite prieš iš naujo įkeldami
Kiekvieną pakeitimą pirmiausia patikrinkite su nginx -t ir tik tada iš naujo įkelkite Nginx. Po to elgseną iš išorės išbandykite įprastomis užklausomis į savo domeną:
sudo nginx -t && sudo systemctl reload nginx
# Tikimasi: 403 arba 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
# Tikimasi: 404, nes vykdomas tik Front-Controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Kurie blokai perduoda PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Prieš kiekvieną didesnį pakeitimą išsaugokite pilną nginx -T išvestį su data. Joje yra visa veiksminga konfigūracija, įskaitant visus įtrauktus failus. Jei po pakeitimo kas nors nepavyksta, palyginimas iš karto parodo, kas skiriasi. Kitos kritinės spragos atveju taip turėsite ir patikimą inventorizacijos pagrindą nereikalaujant pirma rinkti failų.
Konfigūraciją pirmiausia keiskite testinėje aplinkoje arba serverio bloko kopijoje. Pamirštas įėjimo taškas, pavyzdžiui, mokėjimo atgalinis kvietimas (callback), kitaip pastebimas tik tada, kai nebelieka užsakymų.
Kaip PHP SOS padeda konkrečiai?
- Perskaityti konfigūraciją: visą Nginx konfigūraciją analizuojame su
nginx -Tir kiekvieną PHP perdavimą priskiriame jo svetainei ir baseinui. - Nustatyti įėjimo taškus: kartu su jūsų kūrėjais nustatome, kurie scenarijai turi būti vykdomi, ir užblokuojame likusius.
- Įgyvendinti ir išbandyti: pritaikome serverio blokus ir baseinus, tikriname su
nginx -tir išbandome formas, mokėjimo kelius ir įkėlimus. - Apsaugoti ilgam: konfigūracija tampa mūsų serverio sutvirtinimo ir jūsų serverio dokumentacijos dalimi.
Dažni klausimai
Ar po PHP atnaujinimo reikia iš naujo paleisti Nginx?
PHP pataisai ne, nes Nginx neturi PHP. Iš naujo paleisti reikia visas PHP-FPM paslaugas. Jei kartu keičiate ir Nginx konfigūraciją, prieš tai patikrinkite ją su nginx -t ir tada iš naujo įkelkite Nginx.
Kodėl PHP perduoti tik index.php?
Daugelis šiuolaikinių programų turi vieną įėjimo tašką, Front-Controller. Jei PHP-FPM perduodamas tik šis failas, įterpto PHP failo kitoje vietoje per naršyklę vykdyti nebus įmanoma. Sistemose su daug įėjimo taškų šis šablonas neįmanomas.
Ką duoda security.limit_extensions PHP-FPM?
Nustatymas nurodo, kuriuos failų plėtinius PHP-FPM vykdo kaip scenarijų. Pasak PHP vadovo, numatytieji yra .php .phar; rekomenduojama apriboti iki .php. Tai pagauna žiniatinklio serverio konfigūracijos klaidas, bet jos nepakeičia.
Ar Nginx taisyklės yra apsauga nuo PHP2Shell?
Ne. PHP2Shell slypi PHP interpretatoriuje ir uždaroma tik atnaujinus į ištaisytą versiją ir iš naujo paleidus visus darbinius procesus (worker). Saugi Nginx konfigūracija sumažina atakos paviršių ir apriboja žalą po įsilaužimo.