Aktuální situace: PHP2Shell. Dvě kritické zranitelnosti PHP (CVSS 9.2) umožňují spustit škodlivý kód bez přihlášení. Záplaty od 21. 10. 2026.Co nyní udělat →

Izolace poolů PHP-FPM pro každý web

Vlastní pool pro každý web je začátek, ne konec. Jak zkontrolovat stávající pooly a zabezpečit sockety, přípony, stavové stránky, zdroje a logy.

Stav 5 min čteníHardening a konfigurace

Stručně: Každý web na serveru by měl mít vlastní pool PHP-FPM s vlastním systémovým uživatelem a vlastním socketem. Oddělení se však stane účinným až s detaily: oprávnění k socketu jen pro webový server, security.limit_extensions na .php, žádné veřejně dostupné stavové stránky, vlastní limity pro procesy a dobu běhu a oddělené logy. Před samotným PHP2Shell chrání jen aktualizace; pooly omezují, jak daleko útok sáhne.

Jeden server, mnoho webů, společné riziko

Na mnoha serverech se za léta nashromáždilo to, co nikdo takto neplánoval. Firemní web, e-shop, kampaňová stránka z roku 2019 a testovací systém běží vedle sebe, často pod stejným uživatelem a ve stejném poolu. Pokud je napadena zapomenutá kampaňová stránka, jsou odkryta i data zákazníků e-shopu.

PHP-FPM, správce procesů FastCGI pro PHP, to řeší pooly: skupinami procesů worker s vlastním nastavením. Jak základní princip funguje, vysvětluje stránka PHP-FPM vysvětleno: správce procesů, pooly a workeři. Sestavení oddělených poolů s vlastním uživatelem, vlastní dočasnou složkou a open_basedir popisuje průvodce open_basedir a izolace pro každý web. Tento článek jde o krok dál: Jak zkontrolovat stávající pooly a která nastavení činí oddělení odolným?

Důležité zařazení: u PHP2Shell leží zranitelnosti CVE-2026-109618 a CVE-2026-109619 v interpretu PHP, který sdílejí všechny pooly dané verze PHP. Vlastní pool zneužití nezabrání. Rozhodne ale o tom, zda útočník poté dosáhne jednoho webu, nebo všech.

Pooly PHP-FPM s vlastním uživatelem: zkontrolujte stav

Než cokoli změníte, udělejte si přehled. Následující příkazy jen čtou. Cesty a čísla verzí se podle distribuce liší:

# Všechny pooly s uživatelem a socketem
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf

# Běžící pooly a jejich uživatelé
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'

# Sockety a jejich oprávnění
ls -l /run/php/

# Zkontrolovat konfiguraci na chyby (nic nemění)
sudo php-fpm8.3 -t

Všímejte si tří vzorců: několik webů ve stejném poolu, pooly běžící jako www-data nebo dokonce jako root a sockety zapisovatelné pro všechny uživatele. Každý z nich oddělení podkopává.

Pokud na serveru běží několik verzí PHP, má každá vlastní službu FPM s vlastními adresáři poolů. Zkontrolujte všechny, nejen verzi, kterou jste nastavovali naposledy. Právě staré verze se zapomenutými pooly jsou riziko, protože se často neudržují ani po aktualizacích nerestartují. Upozornění najdete v článku Více verzí PHP na jednom serveru. Výsledek zaznamenejte do malé tabulky: web, pool, uživatel, socket, verze PHP. Tento přehled je zároveň základem každé pozdější změny.

Nejdůležitější nastavení pro každý pool

Následující přehled vychází z konfigurace FPM v příručce PHP. Hodnoty pro procesy a časy se řídí vaší zátěží a aplikací:

Nastavení Účel Doporučení
user, group uživatel workerů vlastní uživatel pro každý web, nikdy root
listen.owner, listen.group, listen.mode oprávnění k socketu jen skupina webového serveru, režim 0660
listen.acl_users přístup přes ACL místo vlastníka zapsat jen uživatele webového serveru
security.limit_extensions spustitelné přípony omezit na .php (výchozí .php .phar)
clear_env vyprázdnění proměnných prostředí ponechat na výchozím yes
pm.max_children nejvyšší počet workerů omezit tak, aby jeden web nezatížil server
request_terminate_timeout ukončení zaseknutých požadavků nastavit podle nejdelšího legitimního požadavku
pm.status_path, ping.path stavová a ping stránka nastavit jen v případě potřeby a zabezpečit interně
slowlog, request_slowlog_timeout protokol pomalých požadavků pro každý pool do vlastního souboru

Výňatek pro zpevněný pool vypadá takto:

; /etc/php/8.3/fpm/pool.d/shop.conf (výňatek)
[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

Nastavení jako memory_limit nebo session.upload_progress.enabled zadáváte pro každý pool pomocí php_admin_value a php_admin_flag. Podle příručky nelze tyto hodnoty přepsat pomocí ini_set(). Více v článku php_admin_value: uzamknutí nastavení proti přepsání.

Správně zařadit stavové stránky a obměnu workerů

pm.status_path a ping.path jsou aktivní jen tehdy, když jsou nastaveny. Stavová stránka ukazuje běžící požadavky, procesy a vytížení. Pro monitoring je to užitečné, veřejně dostupná je však zbytečným zdrojem informací pro útočníky. Tyto cesty ve webovém serveru předávejte jen interním adresám. Jak bezpečně spolupracují Nginx a PHP-FPM, popisuje Zabezpečení Nginx s PHP-FPM.

pm.max_requests určuje, po kolika požadavcích se worker restartuje; výchozí hodnota je 0, tedy nikdy. Hodnota větší než nula pomáhá proti pomalu rostoucí spotřebě paměti. Jako ochrana proti PHP2Shell není určena: php.net ji mezi přechodnými opatřeními neuvádí a zdůrazňuje, že plně chrání jen aktualizace. Po aktualizaci navíc platí: všichni workeři všech poolů se musí restartovat, protože staré procesy by jinak běžely dál se zranitelným kódem.

Tip

Udržujte šablonu pro nové pooly a každý nový web zakládejte podle ní. Oddělení pak vzniká automaticky, místo aby se po létech dodatečně dopracovávalo.

Typické chyby při přechodu

Nejčastější chybou je nový pool, který sice běží pod vlastním uživatelem, ale jeho soubory dál patří www-data. Pool pak nesmí zapisovat tam, kde musí, a někdo nastaví oprávnění příliš velkoryse. Předem vyjasněte, které složky musí být zapisovatelné. Druhou chybou jsou plánované úlohy, které dál běží pod starým uživatelem a vytvářejí soubory se špatným vlastníkem. Třetí je společná složka session pro všechny pooly, která oddělení opět ruší.

Po každém přechodu otestujte přihlášení, nahrávání, cache a plánované úlohy webu a sledujte chybový log poolu.

Jak konkrétně pomáhá PHP SOS?

  • Zmapujeme všechny pooly, uživatele, sockety a nastavení a ukážeme, kde si weby dnes sdílejí procesy nebo oprávnění.
  • Pro každý web nastavíme zpevněný pool s oprávněními k socketu, omezením přípon, limity zdrojů a vlastními logy.
  • Zabezpečíme stavové stránky, kontrolovaně restartujeme všechny workery a otestujeme každý web.
  • V rámci zpevnění serveru oddělení zdokumentujeme jako šablonu pro budoucí projekty.

Časté dotazy

Proč by měl mít každý web vlastní pool PHP-FPM?

Vlastní pool s vlastním systémovým uživatelem odděluje weby na úrovni procesů a oprávnění k souborům. Když je jeden web napaden, kód nemůže bez dalšího číst soubory ostatních. Navíc lze limity a nastavení určit pro každý web zvlášť.

Chrání vlastní pool před PHP2Shell?

Ne. Zranitelnosti jsou v samotném interpretu PHP, který sdílejí všechny pooly. Ochranu poskytuje jen aktualizace na opravenou verzi s restartem všech workerů. Oddělené pooly ale omezují, jak daleko úspěšný útok sáhne.

Co způsobuje security.limit_extensions?

Podle příručky PHP nastavení omezuje, které přípony souborů PHP-FPM spouští jako hlavní skript. Výchozí jsou .php a .phar. Kdo je omezí na .php, zabrání tomu, aby se například nahrané obrazové soubory spouštěly jako PHP, pokud je webový server nesprávně nakonfigurován.

Je stavová stránka PHP-FPM nebezpečná?

Prozrazuje podrobnosti o běžících procesech a požadavcích. Pokud je veřejně dostupná bez ochrany, usnadňuje útočníkům orientaci. Podle příručky je aktivní jen tehdy, když je nastaveno pm.status_path. Webový server by pak měl cestu omezit na interní adresy.

Zdroje

Okamžitý kontakt

Každá hodina se počítá. Napište nám hned.

Stručně popište systém, verzi PHP a pozorování. Ozveme se, vyjasníme situaci a budeme pracovat na dálku, dokud nebude incident uzavřen a zdokumentován.

Okamžitá pomoc