Stručne: Každý web na serveri by mal mať vlastný pool PHP-FPM s vlastným systémovým používateľom a vlastným socketom. Oddelenie je však účinné až s podrobnosťami: práva k socketu len pre webový server, security.limit_extensions na .php, žiadne verejne dostupné stavové stránky, vlastné limity pre procesy a dobu behu a oddelené protokoly. Pred samotným PHP2Shell chráni iba aktualizácia; pooly obmedzujú, ako ďaleko siahne útok.
Jeden server, veľa webov, spoločné riziko
Na mnohých serveroch sa za roky nahromadilo to, čo nikto takto neplánoval. Firemný web, e-shop, kampaňová stránka z roku 2019 a testovací systém bežia vedľa seba, často pod tým istým používateľom a v tom istom poole. Ak sa kompromituje zabudnutá kampaňová stránka, sú otvorené aj údaje zákazníkov e-shopu.
PHP-FPM, FastCGI správca procesov PHP, to rieši pomocou poolov: skupín procesov workerov s vlastnými nastaveniami. Ako základný princíp funguje, vysvetľuje PHP-FPM vysvetlený: správca procesov, pooly a workeri. Zostavenie oddelených poolov s vlastným používateľom, vlastným dočasným priečinkom a open_basedir opisuje sprievodca open_basedir a izolácia pre každý web. Tento príspevok ide o krok ďalej: ako skontrolujete existujúce pooly a ktoré nastavenia robia oddelenie spoľahlivým?
Dôležité pre zaradenie: pri PHP2Shell sú zraniteľnosti CVE-2026-109618 a CVE-2026-109619 v interpreteri PHP, ktorý zdieľajú všetky pooly jednej verzie PHP. Vlastný pool zneužitiu nezabráni. Rozhoduje však o tom, či útočník potom dosiahne jeden web, alebo všetky.
Pooly PHP-FPM s vlastným používateľom: kontrola stavu
Skôr než niečo zmeníte, urobte si prehľad. Nasledujúce príkazy len čítajú. Cesty a čísla verzií sa podľa distribúcie líšia:
# Všetky pooly s používateľom a socketom
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf
# Bežiace pooly a ich používatelia
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'
# Sockety a ich práva
ls -l /run/php/
# Skontrolovať konfiguráciu na chyby (nič nemení)
sudo php-fpm8.3 -t
Všímajte si tri vzory: viacero webov v tom istom poole, pooly, ktoré bežia ako www-data alebo dokonca ako root, a sockety, do ktorých smú zapisovať všetci používatelia. Každý z nich oslabuje oddelenie.
Ak na serveri beží viacero verzií PHP, každá má vlastnú službu FPM s vlastnými adresármi poolov. Skontrolujte všetky, nielen verziu, ktorú ste nastavovali naposledy. Práve staré verzie so zabudnutými poolmi sú riziko, pretože sa často neudržiavajú ani po aktualizáciách nereštartujú. Pokyny nájdete v článku Viacero verzií PHP na jednom serveri. Výsledok zapíšte do malej tabuľky: web, pool, používateľ, socket, verzia PHP. Tento prehľad je zároveň základom pre každú neskoršiu zmenu.
Najdôležitejšie nastavenia pre každý pool
Nasledujúci prehľad vychádza z konfigurácie FPM v príručke PHP. Hodnoty pre procesy a časy sa riadia vaším zaťažením a aplikáciou:
| Nastavenie | Účel | Odporúčanie |
|---|---|---|
user, group |
používateľ workerov | vlastný používateľ pre každý web, nikdy root |
listen.owner, listen.group, listen.mode |
práva k socketu | len skupina webového servera, režim 0660 |
listen.acl_users |
prístup cez ACL namiesto vlastníka | zapísať len používateľa webového servera |
security.limit_extensions |
vykonateľné prípony | obmedziť na .php (štandardne .php .phar) |
clear_env |
vyprázdnenie premenných prostredia | nechať na štandardnom yes |
pm.max_children |
maximálny počet workerov | obmedziť tak, aby jeden web server nevyťažil |
request_terminate_timeout |
zrušenie visiacich požiadaviek | nastaviť podľa najdlhšej legitímnej požiadavky |
pm.status_path, ping.path |
stavová a ping stránka | nastaviť len ak je potrebné a zabezpečiť interne |
slowlog, request_slowlog_timeout |
protokol pomalých požiadaviek | pre každý pool do vlastného súboru |
Výňatok pre sprísnený pool vyzerá takto:
; /etc/php/8.3/fpm/pool.d/shop.conf (výňatok)
[shop]
user = shop
group = shop
listen = /run/php/shop.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 60s
security.limit_extensions = .php
clear_env = yes
slowlog = /var/log/php/shop.slow.log
request_slowlog_timeout = 10s
php_admin_value[error_log] = /var/log/php/shop.error.log
Nastavenia ako memory_limit alebo session.upload_progress.enabled určujete pre každý pool pomocou php_admin_value a php_admin_flag. Podľa príručky sa tieto hodnoty nedajú prepísať cez ini_set(). Viac v článku php_admin_value: zablokovanie nastavení proti prepísaniu.
Stavové stránky a výmena workerov správne zaradiť
pm.status_path a ping.path sú aktívne len vtedy, ak sú nastavené. Stavová stránka ukazuje bežiace požiadavky, procesy a vyťaženie. Pre monitoring je to užitočné, verejne dostupná je však zbytočným zdrojom informácií pre útočníkov. Tieto cesty presmerúvajte vo webovom serveri len pre interné adresy. Ako Nginx a PHP-FPM bezpečne spolupracujú, opisuje Zabezpečenie Nginx s PHP-FPM.
pm.max_requests určuje, po koľkých požiadavkách sa worker znova spustí; štandard je 0, teda nikdy. Hodnota väčšia ako nula pomáha proti pomaly rastúcej spotrebe pamäte. Ako ochrana pred PHP2Shell nie je určená: php.net ju neuvádza medzi prechodnými opatreniami a zdôrazňuje, že úplne chráni len aktualizácia. Po aktualizácii navyše platí: všetci workeri všetkých poolov sa musia reštartovať, pretože staré procesy inak bežia ďalej so zraniteľným kódom.
Udržiavajte šablónu pre nové pooly a každý nový web vytvárajte s ňou. Oddelenie tak vznikne automaticky, namiesto toho, aby sa dodatočne dobiehalo o roky neskôr.
Typické chyby pri prechode
Najčastejšou chybou je nový pool, ktorý síce beží pod vlastným používateľom, ale jeho súbory naďalej patria www-data. Potom pool nesmie zapisovať tam, kde musí, a niekto nastaví práva príliš voľne. Vopred si ujasnite, ktoré priečinky musia byť zapisovateľné. Druhou chybou sú plánované úlohy, ktoré bežia ďalej pod starým používateľom a vytvárajú súbory so zlým vlastníkom. Treťou je spoločný priečinok relácií pre všetky pooly, ktorý oddelenie opäť ruší.
Po každom prechode otestujte prihlásenie, uploady, cache a plánované úlohy webu a sledujte chybový protokol poolu.
Ako konkrétne pomáha PHP SOS?
- Zachytíme všetky pooly, používateľov, sockety a nastavenia a ukážeme, kde si weby dnes zdieľajú procesy alebo práva.
- Pre každý web vytvoríme sprísnený pool s právami k socketu, hranicou prípon, limitmi zdrojov a vlastnými protokolmi.
- Zabezpečíme stavové stránky, riadene reštartujeme všetkých workerov a otestujeme každý web.
- V rámci sprísnenia servera zdokumentujeme oddelenie ako šablónu pre budúce projekty.
Časté otázky
Prečo by mal mať každý web vlastný pool PHP-FPM?
Vlastný pool s vlastným systémovým používateľom oddeľuje weby na úrovni procesov a práv k súborom. Ak sa jeden web kompromituje, kód nemôže bez ďalšieho čítať súbory ostatných. Okrem toho sa dajú limity a nastavenia určiť pre každý web osobitne.
Chráni vlastný pool pred PHP2Shell?
Nie. Zraniteľnosti sú v samotnom interpreteri PHP, ktorý spoločne používajú všetky pooly. Ochranu poskytuje len aktualizácia na opravenú verziu s reštartom všetkých workerov. Oddelené pooly však obmedzujú, ako ďaleko siahne úspešný útok.
Čo spôsobuje security.limit_extensions?
Podľa príručky PHP nastavenie obmedzuje, ktoré prípony súborov PHP-FPM vykoná ako hlavný skript. Štandardne je to .php a .phar. Kto ich obmedzí na .php, zabráni tomu, aby sa napríklad nahrané obrazové súbory vykonávali ako PHP, ak je webový server nesprávne nakonfigurovaný.
Je stavová stránka PHP-FPM nebezpečná?
Prezrádza podrobnosti o bežiacich procesoch a požiadavkách. Ak je bez ochrany verejne dostupná, uľahčuje útočníkom orientáciu. Podľa príručky je aktívna len vtedy, ak je nastavené pm.status_path. Potom by mal webový server obmedziť cestu na interné adresy.