Aktuálna situácia: PHP2Shell. Dve kritické zraniteľnosti PHP (CVSS 9.2) umožňujú spustenie škodlivého kódu bez prihlásenia. Záplaty od 21. 10. 2026.Čo treba urobiť teraz →

Životný cyklus požiadavky: štart, vykonanie, shutdown

Ako PHP spúšťa procesy, vybavuje požiadavky a medzitým upratuje. S fázami MINIT až MSHUTDOWN a súvislosťou s CVE-2026-109619.

Stav 5 min čítaniaZnalosti o PHP

Stručne: Životný cyklus požiadavky PHP má dve úrovne. Raz na proces PHP načíta svoje rozšírenia (fáza MINIT) a na konci ich opäť odbúra (MSHUTDOWN). Pre každú jednotlivú požiadavku existuje vlastná štartovacia a upratovacia fáza (RINIT a RSHUTDOWN). Vo webovej prevádzke worker vybavuje mnoho požiadaviek po sebe, preto musí PHP medzi dvoma požiadavkami spoľahlivo vynulovať všetok stav požiadavky. Práve na tejto hranici ležala CVE-2026-109619, druhá zraniteľnosť PHP2Shell.

Dve úrovne: proces a požiadavka

Pre netechnikov pomáha prirovnanie: worker PHP je ako okienko na úrade. Okienko sa ráno raz zariadi, s formulármi, pečiatkami a počítačom. Potom prichádza po sebe mnoho návštevníkov. Po každom návštevníkovi musí byť stôl prázdny, aby ďalší nevidel cudzie podklady.

PHP funguje rovnako. Pri štarte procesu načíta rozšírenia a nastavenia, ktoré sú pre všetky požiadavky rovnaké. Každá požiadavka potom dostane vlastnú pamäť, vlastné premenné a vlastné údaje požiadavky, ako $_GET, $_POST alebo $_FILES. Na konci požiadavky PHP túto pamäť uvoľní. PHP Internals Book to zhŕňa stručne: PHP na konci každej požiadavky zabudne pamäť požiadavky.

Fázy životného cyklu požiadavky PHP v prehľade

Názvy pochádzajú z vnútra PHP; každé rozšírenie sa môže do týchto fáz zapojiť.

Fáza Kedy beží Ako často Typická úloha
MINIT (Module Init) pri štarte procesu raz na proces registrovať rozšírenia, vytvoriť záznamy ini, trvalé údaje
GINIT pri vytváraní globálnych premenných na proces, pri vláknach na vlákno pripraviť globálne premenné rozšírenia
RINIT (Request Init) na začiatku každej požiadavky na požiadavku pripraviť pamäť a stav požiadavky
Vykonanie medzitým na požiadavku beží váš kód PHP
RSHUTDOWN na konci každej požiadavky na požiadavku uvoľniť zdroje požiadavky
PRSHUTDOWN po RSHUTDOWN na požiadavku, zriedka používané posledné upratovacie práce
MSHUTDOWN pri ukončení procesu raz na proces uvoľniť trvalé údaje

Dôležitá veta z PHP Internals Book pre vývojárov rozšírení: globálne premenné sa po každej požiadavke automaticky nevyprázdňujú. Čo musí byť pri každej požiadavke čerstvé, patrí vynulovať v RINIT. Toto pravidlo ukazuje, prečo je hranica medzi dvoma požiadavkami bezpečnostne kritickým miestom.

Na konci požiadavky je poradie tiež pevne dané: najprv bežia funkcie shutdown, ktoré ste zaregistrovali pomocou register_shutdown_function(), potom destruktory, potom sa vyprázdni výstupný buffer. Až potom nasleduje RSHUTDOWN.

Prečo sa workery používajú opakovane

V príkazovom riadku vybaví proces presne jednu požiadavku a skončí. Vo webovej prevádzke by to bolo príliš pomalé. PHP-FPM, Apache s mod_php, LiteSpeed a FrankenPHP preto držia workery, ktoré obsluhujú mnoho požiadaviek za sebou. MINIT potom beží raz, RINIT a RSHUTDOWN tisíckrát. Ako FPM riadi svojich workerov, vysvetľuje PHP-FPM vysvetlené.

V PHP-FPM určuje pm.max_requests, po koľkých požiadavkách sa worker reštartuje. Štandard je 0, teda neobmedzene. Príručka toto nastavenie uvádza ako prostriedok proti únikom pamäte v knižniciach tretích strán:

; Pool FPM: reštartovať worker po 500 požiadavkách (proti únikom pamäte)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

Ešte ďalej ide režim workera vo FrankenPHP: tam sa celá aplikácia spustí raz a zostáva v pamäti. Stav aplikácie potom prežíva aj na úrovni PHP. Príklad ukazuje rozdiel:

<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // v klasickej prevádzke FPM je pri každej požiadavke opäť 0
    return ++$zaehler;     // v režime workera hodnota naďalej rastie naprieč požiadavkami
}
echo anfrageZaehler();

V klasickej prevádzke vypíše volanie vždy 1, pretože PHP zahodí pamäť požiadavky. V režime workera hodnota rastie, kým worker žije. Dokumentácia FrankenPHP preto radí stav viazaný na požiadavku medzi požiadavkami vedome vynulovať.

Kde útočí CVE-2026-109619

CVE-2026-109619 sa netýka vášho aplikačného kódu, ale samotného PHP. Podľa php.net mohol interný stav patriaci k požiadavke prežiť uvoľnenie svojej pamäte. Neskoršia požiadavka v tom istom workeri ho mohla použiť skôr, než ho inicializácia vynulovala. V obraze: na stole okienka zostal ležať lístok, ktorý mal byť už odložený. Podrobnosti o zneužití zámerne nezverejňujeme; zaradenie je uvedené v CVE-2026-109619.

Prechodné opatrenia php.net k tejto zraniteľnosti sa opierajú o potvrdené cesty, nie sú úplnou ochranou:

  1. session.upload_progress.enabled=0 v php.ini, poole FPM alebo konfigurácii servera, potom reštartovať všetky workery; nie cez ini_set() ani .user.ini.
  2. Na PHP 8.5 navyše fatal_error_backtraces=0 a reštart.
  3. Odstrániť neplatné alebo zastarané nastavenia pre každú požiadavku, ktoré pred inicializáciou vytvárajú hlásenia. Hlásenia iba skryť podľa php.net nepomáha.
  4. Opraviť v aplikácii fatálne chyby a prekročenia času, ktoré sa dajú vyvolať zvonku, aj v callbackoch, pri spracovaní nahrávaní a importov.

Skutočným riešením je aktualizácia na PHP 8.5.12, 8.4.27, 8.3.36 alebo 8.2.35. Pretože chyba sídli v bežiacom workeri, účinkuje až po reštarte všetkých procesov; kroky sú uvedené v Reštart PHP-FPM a všetkých workerov.

Tip

Po reštarte skontrolujte pomocou ps -eo pid,lstart,cmd | grep php, či sa všetky procesy PHP spustili po čase aktualizácie.

Čo životný cyklus znamená pre prevádzku a vývoj

Z modelu sa dajú odvodiť jasné pravidlá. Pre administrátorov: aktualizácia je účinná, až keď už nebeží žiadny proces so starým kódom. Každý spôsob prevádzky s dlhožijúcimi workermi potrebuje zdokumentovaný reštart. Pre vývojárov: kód, ktorý beží v režimoch workera, nesmie nechávať stav viazaný na požiadavku v statických premenných alebo singletonoch, napríklad údaje používateľa alebo oprávnenia. Pre oboch platí: uchovajte logy od 20. 10. 2026, aby sa neskôr dalo posúdiť, či sa pred záplatou niečo stalo.

Ako PHP SOS konkrétne pomáha?

Pomocou služby záplatovania PHP model uplatňujeme v praxi:

  • Určenie spôsobu prevádzky: Zisťujeme, či beží PHP-FPM, mod_php, LiteSpeed, FrankenPHP alebo php-cgi a ako sa workery spravujú.
  • Aktualizácia a reštart: Nasadzujeme opravenú verziu a kontrolovane reštartujeme každý worker.
  • Kontrola procesov: Pomocou časov štartu a verzií dokladujeme, že nezostal žiadny starý proces.
  • Kontrola režimov workera: Pri dlhožijúcich aplikáciách sa pozeráme, či sa stav viazaný na požiadavku čisto nuluje.

Časté otázky

Spúšťa PHP pre každú požiadavku nový proces?

V príkazovom riadku áno, vo webovej prevádzke väčšinou nie. PHP-FPM, Apache s mod_php, LiteSpeed alebo FrankenPHP nechávajú jeden proces vybavovať mnoho požiadaviek po sebe. Medzi požiadavkami PHP upratuje stav požiadavky.

Aký je rozdiel medzi MINIT a RINIT?

MINIT beží raz, keď sa proces PHP spustí, a vytvára trvalé štruktúry. RINIT beží na začiatku každej jednotlivej požiadavky a pripravuje to, čo potrebuje len táto požiadavka.

Pomáha pm.max_requests proti CVE-2026-109619?

php.net častejšie reštarty workerov ako prechodné opatrenie neuvádza. Nastavenie obmedzuje iba to, koľko požiadaviek worker vybaví pred reštartom. Proti zraniteľnosti pomáha aktualizácia s následným reštartom všetkých workerov.

Čo je režim workera vo FrankenPHP?

V režime workera sa aplikácia spustí raz a zostáva v pamäti. Ušetrí to čas, ale statické premenné, globálne premenné a cache sa medzi požiadavkami zachovávajú. Podľa dokumentácie FrankenPHP sa preto stav viazaný na požiadavku musí vedome vynulovať.

Zdroje

Okamžitý kontakt

Každá hodina sa počíta. Napíšte nám hneď.

Stručne opíšte systém, verziu PHP a svoje pozorovanie. Ozveme sa, vyjasníme situáciu a budeme pracovať na diaľku, kým nebude incident uzavretý a zdokumentovaný.

Okamžitá pomoc