Trumpai: Naudojant Nginx su PHP-FPM, PHP2Shell pažeidžiamumas yra PHP-FPM, o ne Nginx. Pataisykite visas PHP-FPM versijas, visiškai paleiskite iš naujo kiekvieną FPM tarnybą ir tai įrodykite procesų paleidimo laikais. Ilgalaikei Nginx PHP-FPM saugai svarbūs atskiri baseinai, neperrašomos reikšmės ir Nginx konfigūracija, kuri nevykdo įkeltų failų.
Kaip Nginx ir PHP-FPM dirba kartu
Nginx statinius failus pateikia pats, o užklausas PHP skriptams per FastCGI perduoda PHP-FPM. PHP-FPM sudaro vienas master procesas ir keli darbiniai procesai (worker), sugrupuoti baseinuose. Vienas darbinis procesas paeiliui apdoroja daug užklausų.
Būtent tai svarbu PHP2Shell atveju. Esant CVE-2026-109619, vidinė užklausos būsena po atminties atlaisvinimo gali išlikti ir būti panaudota vėlesnės užklausos tame pačiame darbiniame procese. Esant CVE-2026-109618, atminties trūkumo (out-of-memory) būsena gali nutraukti PHP užklausos inicializavimo viduryje. Abu pažeidžiamumai įvertinti CVSS v4.0 9,2 ir ištaisyti PHP 8.5.12, 8.4.27, 8.3.36 ir 8.2.35.
Patikrinkite esamą būklę: versijas, baseinus, procesus
Daugelis serverių lygiagrečiai turi kelias PHP-FPM versijas. Toliau pateiktos komandos tik skaito; paketų ir tarnybų pavadinimai yra Debian/Ubuntu ir RHEL šeimos pavyzdžiai.
# Kurios FPM tarnybos veikia?
systemctl list-units --type=service | grep -i fpm
# Kokia versija už jų? (Debian/Ubuntu pavyzdys)
php-fpm8.3 -v
# RHEL šeima
php-fpm -v
# Kuriems lizdams (socket) ar prievadams Nginx perduoda užklausas?
nginx -T 2>/dev/null | grep -E 'fastcgi_pass'
# Baseinai ir jų naudotojai
grep -RhE '^\[|^user|^listen' /etc/php/*/fpm/pool.d/ 2>/dev/null
Palyginkite kiekvieną fastcgi_pass įrašą su veikiančiu baseinu. Taip pamatysite, kuri PHP versija aptarnauja kurią svetainę. Distribucijų paketų atveju pataisą įrodo pakeitimų žurnalas su CVE numeriais, o ne vien versijos numeris.
Dažnas radinys tokiose inventorizacijose yra serveriai, kuriuose per metus įdiegtos kelios PHP versijos, iš kurių realiai naudojama tik viena. Kitos FPM tarnybos vis tiek veikia, dažnai su standartiniais baseinais, klausančiais vietinio lizdo ar prievado. Iš išorės jos paprastai tiesiogiai nepasiekiamos, bet lieka nereikalinga rizika ir pailgina kiekvieną skubų atnaujinimą. Išjunkite ir pašalinkite versijas, kurių nenaudoja jokia svetainė, kai pagal fastcgi_pass įrašus patikrinsite priskyrimą.
Verta pažvelgti ir į baseinų naudotoją. Jei visi baseinai veikia kaip www-data ar nginx, svetainių atskyrimas egzistuoja tik popieriuje. Kas perima vieną svetainę, tas gali skaityti visų kitų failus, pavyzdžiui, konfigūracijos failus su duomenų bazės slaptažodžiais. Atskiri sistemos naudotojai kiekvienam baseinui ir tinkamos failų teisės čia sukuria tikrą ribą. Tai nėra PHP2Shell gydymas, bet gerokai apriboja galimą sėkmingos atakos žalą.
Įdiekite pataisą ir atnaujinkite visus darbinius procesus
Po paketų atnaujinimo iš naujo paleiskite kiekvieną PHP-FPM tarnybą. Reload pakeičia darbinius procesus, bet visiškas paleidimas iš naujo atnaujina ir master procesą ir po saugumo atnaujinimų yra saugus kelias.
# Debian/Ubuntu pavyzdys, kiekvienai įdiegtai versijai
systemctl restart php8.3-fpm
# RHEL šeima
systemctl restart php-fpm
# Kontrolė: master ir darbinių procesų paleidimo laikas
ps -eo pid,lstart,cmd | grep -E 'php-fpm' | grep -v grep
Kiekvienas procesas turi būti paleistas po atnaujinimo. Plačiau aprašyta puslapyje PHP-FPM ir visų darbinių procesų paleidimas iš naujo.
Pataisa nepašalina kenkėjiškos programinės įrangos, įterptos anksčiau. Išsaugokite Nginx prieigos ir klaidų žurnalus nuo 2026-10-20 ir patikrinkite svetaines dėl pėdsakų.
Nustatykite pereinamąsias reikšmes baseine
Kol versija nepataisyta, php.net rekomenduoja pereinamąsias priemones. Jos nesuteikia visiškos apsaugos. PHP-FPM reikšmes nustatykite baseine per php_admin_value arba php_admin_flag, tada programos jų per ini_set() perrašyti negali.
; Pavyzdys: /etc/php/8.3/fpm/pool.d/website.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
Be įkėlimo funkcijos php.net nurodo post_max_size = 8M. PHP 8.5 papildomai nustatoma fatal_error_backtraces=0. Nuorodai dėl memory_limit=-1 jums reikia atminties ribos cgroup lygmeniu, pavyzdžiui, per MemoryMax FPM tarnybos systemd papildyme. Nginx pritaikykite client_max_body_size prie naujos post_max_size, kad naudotojai gautų suprantamus klaidų pranešimus. Po to iš naujo paleiskite FPM.
Nuolat gerinkite Nginx PHP-FPM saugą
Be ūmaus atvejo, padeda kelios pagrindinės taisyklės, ribojančios žalą būsimų pažeidžiamumų atveju:
- Vienas baseinas kiekvienai svetainei su savu sistemos naudotoju, savu lizdu ir savo
open_basedirriba. Daugiau puslapyje open_basedir ir izoliacija kiekvienai svetainei. - Lizdo teisės nustatomos taip, kad prieiti galėtų tik Nginx, FPM prievadų į išorę neatidarykite.
- Vykdykite tik egzistuojančius skriptus: PHP location nustatykite
try_files $uri =404;, kad Nginx nepersiųstų PHP kelių, kurių nėra. - Nevykdykite įkėlimų: įkėlimų kataloguose uždrauskite PHP vykdymą. Nurodymų rasite puslapyje Saugus failų įkėlimų įgyvendinimas.
- Apsaugokite būsenos puslapius:
pm.status_pathirping.pathpadarykite pasiekiamus tik viduje.
# Pavyzdys: PHP nevykdomas įkėlimų kataloge
location ~* ^/uploads/.*\.php$ {
deny all;
}
Kiekvieną pakeitimą patikrinkite su nginx -t, prieš perkraudami Nginx. Be to, užfiksuokite, kokie baseinai, versijos ir ypatingos taisyklės yra serveryje. Ši trumpa dokumentacija kitą kartą atsiradus kritiniam pažeidžiamumui sutaupo brangaus laiko, nes niekam nereikia ieškoti, kuri svetainė prie kurio baseino priklauso.
Kaip PHP SOS padeda konkrečiai?
- Priskiriame kiekvieną svetainę jos baseinui ir PHP versijai ir tikriname tikrąją paketų būseną.
- Pataisome, iš naujo paleidžiame visas FPM tarnybas ir procesų paleidimo laikus dokumentuojame kaip įrodymą.
- Iki pataisos pereinamąsias reikšmes nustatome kiekvienam baseinui ir apribojame atmintį per cgroup.
- Pagal serverio sutvirtinimą tvarkingai atskiriame baseinus, teises ir įkėlimų katalogus.
- Įtarus įsilaužimą perimame incidentų valdymą.
Dažni klausimai
Ar po atnaujinimo PHP-FPM pakanka systemctl reload?
Reload atnaujina darbinius procesus, bet pagrindinis (master) procesas gali išlaikyti senąjį kodą. Po saugumo atnaujinimo saugiausia visiškai paleisti tarnybą iš naujo. Po to patikrinkite master ir darbinių procesų paleidimo laiką.
Ar po PHP atnaujinimo reikia iš naujo paleisti Nginx?
Pats Nginx PHP neturi ir dėl PHP pataisos paleisti iš naujo jo nereikia. Iš naujo turi būti paleistos visos PHP-FPM tarnybos. Jei keičiate Nginx konfigūraciją, prieš perkraudami patikrinkite ją su nginx -t.
Kodėl kiekvienai svetainei atskiras baseinas?
Kiekvienas baseinas veikia su savu naudotoju ir savomis ribomis. Jei viena svetainė kompromituojama, užpuolikas nelengvai patenka prie kitų failų. Be to, pereinamąsias reikšmes galima nustatyti tikslingai kiekvienam baseinui.
Ar pm.max_requests yra apsauga nuo PHP2Shell?
Ne, jis nepakeičia pataisos. Reikšmė riboja, kiek užklausų darbinis procesas apdoroja, kol bus pakeistas. Tai gali sutrumpinti būsenų gyvavimą procese, bet tai nėra php.net nurodyta pagalbos priemonė.