Röviden: A szerveren lévő minden weboldalnak saját PHP-FPM-poolja legyen saját rendszerfelhasználóval és saját socketkel. Az elkülönítés azonban csak a részletekkel válik hatékonnyá: a socket jogai csak a webszervernek, a security.limit_extensions értéke .php, nincsenek nyilvánosan elérhető státuszoldalak, saját határok a folyamatokra és a futási időre, valamint külön naplók. Magától a PHP2Shelltől csak a frissítés véd; a poolok korlátozzák, meddig jut el egy támadás.
Egy szerver, sok weboldal, közös kockázat
Sok szerveren az évek során olyan állapot alakult ki, amelyet senki sem így tervezett. A céges weboldal, a webshop, egy 2019-es kampányoldal és egy tesztrendszer egymás mellett fut, gyakran ugyanazon felhasználó alatt és ugyanabban a poolban. Ha a megfeledkezett kampányoldalt megtámadják, a webshop ügyféladatai is szabadon állnak.
A PHP-FPM, a PHP FastCGI-folyamatkezelője, ezt poolokkal oldja meg: saját beállításokkal rendelkező worker-folyamatok csoportjaival. Hogyan működik az alapelv, azt a PHP-FPM magyarázata: folyamatkezelő, poolok és workerek írja le. A saját felhasználóval, saját temp-mappával és open_basedir-rel rendelkező elkülönített poolok felépítését az open_basedir és elkülönítés weboldalanként útmutató tárgyalja. Ez a cikk egy lépéssel továbbmegy: hogyan ellenőrizze a meglévő poolokat, és mely beállítások teszik terhelhetővé az elkülönítést?
A besoroláshoz fontos: a PHP2Shellnél a CVE-2026-109618 és CVE-2026-109619 rések a PHP-értelmezőben vannak, amelyen egy PHP-verzió összes poolja osztozik. Egy saját pool nem akadályozza meg a kihasználást. Azt viszont eldönti, hogy a támadó utána egy vagy az összes weboldalt eléri-e.
PHP-FPM-poolok saját felhasználóval: a meglévő állapot ellenőrzése
Mielőtt bármit módosítana, szerezzen áttekintést. A következő parancsok csak olvasnak. Az útvonalak és verziószámok disztribúciónként eltérnek:
# Az összes pool felhasználóval és sockettel
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf
# Futó poolok és felhasználóik
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'
# Socketek és jogaik
ls -l /run/php/
# A konfiguráció hibáinak ellenőrzése (nem módosít semmit)
sudo php-fpm8.3 -t
Három mintára figyeljen: több weboldal ugyanabban a poolban, www-data vagy éppen root alatt futó poolok, és minden felhasználó számára írható socketek. Mindegyik aláássa az elkülönítést.
Ha a szerveren több PHP-verzió fut, mindegyiknek saját FPM-szolgáltatása és saját pool-könyvtárai vannak. Mindet ellenőrizze, ne csak azt a verziót, amelyet utoljára állított be. Éppen a régi verziók elfelejtett poolokkal jelentenek kockázatot, mert gyakran sem nem gondozzák, sem nem indítják újra őket frissítések után. Erről tudnivalók a Több PHP-verzió egy szerveren cikkben vannak. Rögzítse az eredményt egy kis táblázatban: weboldal, pool, felhasználó, socket, PHP-verzió. Ez az áttekintés egyúttal minden későbbi módosítás alapja.
A legfontosabb beállítások poolonként
A következő áttekintés a PHP kézikönyv FPM-konfigurációjára támaszkodik. A folyamatok és idők értékei az Ön terheléséhez és alkalmazásához igazodnak:
| Beállítás | Cél | Ajánlás |
|---|---|---|
user, group |
a workerek felhasználója | weboldalanként saját felhasználó, soha nem root |
listen.owner, listen.group, listen.mode |
a socket jogai | csak a webszerver csoportja, 0660 mód |
listen.acl_users |
hozzáférés ACL-lel a tulajdonos helyett | csak a webszerver felhasználóját beírni |
security.limit_extensions |
végrehajtható végződések | a .php-re korlátozni (alapértelmezés .php .phar) |
clear_env |
környezeti változók ürítése | az alapértelmezett yes értéken hagyni |
pm.max_children |
a workerek legnagyobb száma | úgy korlátozni, hogy egy weboldal ne terhelje le a szervert |
request_terminate_timeout |
a megakadt kérések megszakítása | a leghosszabb jogos kéréshez igazítva beállítani |
pm.status_path, ping.path |
státusz- és ping-oldal | csak akkor beállítani, ha szükséges, és belsőleg védeni |
slowlog, request_slowlog_timeout |
a lassú kérések naplója | poolonként saját fájlba |
Egy keményített pool részlete így néz ki:
; /etc/php/8.3/fpm/pool.d/shop.conf (részlet)
[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
Az olyan beállításokat, mint a memory_limit vagy a session.upload_progress.enabled, poolonként a php_admin_value és a php_admin_flag segítségével állítsa be. A kézikönyv szerint ezeket az értékeket nem lehet ini_set()-tel felülírni. Bővebben a php_admin_value: beállítások zárolása a felülírás ellen cikkben.
A státuszoldalak és a workerváltás helyes besorolása
A pm.status_path és a ping.path csak akkor aktív, ha be van állítva. A státuszoldal a futó kéréseket, folyamatokat és a terheltséget mutatja. A monitoringhoz ez hasznos, nyilvánosan elérhetően viszont felesleges információforrás a támadóknak. Ezeket az útvonalakat a webszerverben csak belső címeknek továbbítsa. Hogyan működik együtt biztonságosan az Nginx és a PHP-FPM, azt a Az Nginx biztonságossá tétele PHP-FPM-mel írja le.
A pm.max_requests azt határozza meg, hány kérés után indul újra egy worker; az alapértelmezés a 0, vagyis soha. A nullánál nagyobb érték a lassan növekvő memóriahasználat ellen segít. A PHP2Shell elleni védelemnek nem szánták: a php.net nem említi az átmeneti intézkedések között, és hangsúlyozza, hogy csak a frissítés véd teljesen. A frissítés után ezenkívül érvényes: az összes pool összes workerét újra kell indítani, mert a régi folyamatok különben a sebezhető kóddal futnak tovább.
Tartson fenn egy sablont az új poolokhoz, és minden új weboldalt ezzel hozzon létre. Így az elkülönítés magától létrejön, ahelyett hogy évekkel később utólag kellene pótolni.
Tipikus hibák az átállásnál
A leggyakoribb hiba egy új pool, amely ugyan saját felhasználó alatt fut, de a fájljai továbbra is a www-data tulajdonában vannak. Ilyenkor a pool nem írhat oda, ahová kell, és valaki túl bőkezűen állítja be a jogokat. Előre tisztázza, mely mappáknak kell írhatónak lenniük. A második hiba az ütemezett feladatok, amelyek tovább a régi felhasználó alatt futnak, és helytelen tulajdonossal hoznak létre fájlokat. A harmadik az összes pool közös session-mappája, amely újra feloldja az elkülönítést.
Minden átállás után tesztelje a weboldal bejelentkezését, feltöltéseit, gyorsítótárait és ütemezett feladatait, és figyelje a pool hibanaplóját.
Hogyan segít konkrétan a PHP SOS?
- Felmérjük az összes poolt, felhasználót, socketet és beállítást, és megmutatjuk, hol osztozik ma a weboldalak között folyamatokon vagy jogokon.
- Weboldalanként egy keményített poolt állítunk be, socketjogokkal, végződéshatárral, erőforráshatárokkal és saját naplókkal.
- Védjük a státuszoldalakat, az összes workert ellenőrzötten újraindítjuk, és minden weboldalt tesztelünk.
- A szerverkeményítés keretében dokumentáljuk az elkülönítést a jövőbeli projektek sablonjaként.
Gyakori kérdések
Miért legyen minden weboldalnak saját PHP-FPM-poolja?
Egy saját rendszerfelhasználóval rendelkező saját pool a folyamatok és a fájljogosultságok szintjén elválasztja egymástól a weboldalakat. Ha az egyik weboldalt megtámadják, a kód nem olvashatja el minden további nélkül a többiek fájljait. Ezenkívül a határok és beállítások weboldalanként külön rögzíthetők.
Véd egy saját pool a PHP2Shell ellen?
Nem. A rések magában a PHP-értelmezőben vannak, amelyet az összes pool közösen használ. Védelmet csak a javított verzióra való frissítés nyújt az összes worker újraindításával. Az elkülönített poolok viszont korlátozzák, meddig jut el egy sikeres támadás.
Mit eredményez a security.limit_extensions?
A PHP kézikönyve szerint a beállítás korlátozza, mely fájlvégződéseket hajt végre a PHP-FPM főszkriptként. Az alapértelmezés a .php és a .phar. Aki a .php-re korlátozza, megakadályozza, hogy például a feltöltött képfájlokat PHP-ként hajtsák végre, ha a webszerver hibásan van beállítva.
Veszélyes a PHP-FPM státuszoldala?
Részleteket árul el a futó folyamatokról és kérésekről. Ha védelem nélkül nyilvánosan elérhető, megkönnyíti a támadók tájékozódását. A kézikönyv szerint csak akkor aktív, ha a pm.status_path be van állítva. Ilyenkor a webszervernek az útvonalat belső címekre kell korlátoznia.