Aktualna sytuacja: PHP2Shell. Dwie krytyczne luki w PHP (CVSS 9,2) umożliwiają wykonanie złośliwego kodu bez logowania. Poprawki od 21.10.2026.Co należy teraz zrobić →

Cykl życia żądania: start, wykonanie, shutdown

Jak PHP uruchamia procesy, obsługuje żądania i sprząta między nimi. Z fazami od MINIT do MSHUTDOWN i związkiem z CVE-2026-109619.

Stan 5 min czytaniaWiedza o PHP

Krótko: Cykl życia żądania PHP ma dwa poziomy. Raz na proces PHP ładuje swoje rozszerzenia (faza MINIT) i na końcu je zwalnia (MSHUTDOWN). Dla każdego pojedynczego żądania istnieje osobna faza startu i sprzątania (RINIT i RSHUTDOWN). W pracy webowej worker obsługuje wiele żądań po kolei, dlatego PHP musi między dwoma żądaniami niezawodnie resetować cały stan żądania. Właśnie na tej granicy leżała CVE-2026-109619, druga luka PHP2Shell.

Dwa poziomy: proces i żądanie

Dla osób nietechnicznych pomocny jest obraz: worker PHP jest jak okienko w urzędzie. Okienko jest rano raz urządzane, z formularzami, pieczątkami i komputerem. Potem przychodzi po kolei wielu interesantów. Po każdym interesancie biurko musi być puste, aby następny nie zobaczył cudzych dokumentów.

PHP działa tak samo. Przy starcie procesu ładuje rozszerzenia i ustawienia, które są takie same dla wszystkich żądań. Każde żądanie dostaje wtedy własną pamięć, własne zmienne i własne dane żądania, takie jak $_GET, $_POST czy $_FILES. Na końcu żądania PHP zwalnia tę pamięć. PHP Internals Book ujmuje to krótko: PHP zapomina pamięć żądania na końcu każdego żądania.

Fazy cyklu życia żądania PHP w skrócie

Nazwy pochodzą z wnętrza PHP; każde rozszerzenie może się podpiąć pod te fazy.

Faza Kiedy się wykonuje Jak często Typowe zadanie
MINIT (Module Init) przy starcie procesu raz na proces rejestracja rozszerzeń, zakładanie wpisów ini, dane trwałe
GINIT przy zakładaniu zmiennych globalnych na proces, przy wątkach na wątek przygotowanie zmiennych globalnych rozszerzenia
RINIT (Request Init) na początku każdego żądania na żądanie przygotowanie pamięci i stanu żądania
Wykonanie pomiędzy na żądanie działa Państwa kod PHP
RSHUTDOWN na końcu każdego żądania na żądanie zwolnienie zasobów żądania
PRSHUTDOWN po RSHUTDOWN na żądanie, rzadko używany ostatnie prace porządkowe
MSHUTDOWN przy zakończeniu procesu raz na proces zwolnienie danych trwałych

Ważne zdanie z PHP Internals Book dla twórców rozszerzeń: zmienne globalne nie są automatycznie czyszczone po każdym żądaniu. To, co ma być świeże dla każdego żądania, należy zresetować w RINIT. Ta reguła pokazuje, dlaczego granica między dwoma żądaniami jest miejscem krytycznym dla bezpieczeństwa.

Na końcu żądania kolejność jest również ustalona: najpierw wykonują się funkcje shutdown zarejestrowane za pomocą register_shutdown_function(), potem destruktory, następnie opróżniany jest bufor wyjścia. Dopiero po tym następuje RSHUTDOWN.

Dlaczego workery są używane ponownie

W wierszu poleceń proces obsługuje dokładnie jedno żądanie i kończy się. W pracy webowej byłoby to zbyt wolne. PHP-FPM, Apache z mod_php, LiteSpeed i FrankenPHP utrzymują więc workery, które obsługują wiele żądań jedno po drugim. MINIT wykonuje się wtedy raz, a RINIT i RSHUTDOWN tysiące razy. Jak FPM steruje swoimi workerami, wyjaśnia strona PHP-FPM wyjaśniony.

W PHP-FPM pm.max_requests określa, po ilu żądaniach worker jest restartowany. Domyślnie jest to 0, czyli bez ograniczenia. Podręcznik podaje to ustawienie jako środek przeciw wyciekom pamięci w bibliotekach podmiotów trzecich:

; pula FPM: restartować workera po 500 żądaniach (przeciw wyciekom pamięci)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

Jeszcze dalej idzie tryb workera we FrankenPHP: tam cała aplikacja uruchamia się raz i pozostaje w pamięci. Stan aplikacji przetrwa wtedy także na poziomie PHP. Przykład pokazuje różnicę:

<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // w klasycznej pracy FPM dla każdego żądania znów 0
    return ++$zaehler;     // w trybie workera wartość liczy dalej między żądaniami
}
echo anfrageZaehler();

W trybie klasycznym wywołanie zawsze wypisuje 1, ponieważ PHP odrzuca pamięć żądania. W trybie workera wartość rośnie, dopóki worker żyje. Dokumentacja FrankenPHP radzi więc świadomie resetować stan związany z żądaniem między żądaniami.

Gdzie uderza CVE-2026-109619

CVE-2026-109619 nie dotyczy kodu Państwa aplikacji, lecz samego PHP. Według php.net wewnętrzny stan należący do żądania mógł przetrwać zwolnienie swojej pamięci. Późniejsze żądanie w tym samym workerze mogło go użyć, zanim inicjalizacja go zresetowała. W obrazie: na biurku w okienku został leżeć świstek, który faktycznie był już sprzątnięty. Szczegółów wykorzystania świadomie nie publikujemy; ocena znajduje się pod hasłem CVE-2026-109619.

Środki przejściowe php.net dotyczące tej luki opierają się na potwierdzonych drogach i nie są pełną ochroną:

  1. session.upload_progress.enabled=0 w php.ini, puli FPM lub konfiguracji serwera, potem zrestartować wszystkie workery; nie przez ini_set() ani .user.ini.
  2. W PHP 8.5 dodatkowo fatal_error_backtraces=0 i restart.
  3. Usunąć nieprawidłowe lub przestarzałe ustawienia dla każdego żądania, które przed inicjalizacją generują komunikaty. Samo ukrywanie komunikatów według php.net nie pomaga.
  4. Naprawić w aplikacji błędy krytyczne i przekroczenia czasu, które można wywołać z zewnątrz, także w callbackach oraz przy przetwarzaniu przesłanych plików i importu.

Właściwym lekarstwem jest aktualizacja do PHP 8.5.12, 8.4.27, 8.3.36 lub 8.2.35. Ponieważ błąd tkwi w działającym workerze, działa ona dopiero po restarcie wszystkich procesów; kroki opisano w artykule Restart PHP-FPM i wszystkich workerów.

Wskazówka

Po restarcie proszę sprawdzić za pomocą ps -eo pid,lstart,cmd | grep php, czy wszystkie procesy PHP wystartowały po momencie aktualizacji.

Co cykl życia oznacza dla eksploatacji i programowania

Z tego modelu można wyprowadzić jasne reguły. Dla administratorów: aktualizacja jest skuteczna dopiero wtedy, gdy nie działa już żaden proces ze starym kodem. Każdy tryb pracy z długowiecznymi workerami potrzebuje udokumentowanego restartu. Dla programistów: kod działający w trybach workera nie może zostawiać stanu związanego z żądaniem w zmiennych statycznych ani singletonach, na przykład danych użytkownika lub uprawnień. Dla obu grup obowiązuje: przechowywać logi od 20.10.2026, aby później móc ocenić, czy przed poprawką coś się wydarzyło.

Jak konkretnie pomaga PHP SOS?

Za pomocą usługi poprawek PHP wdrażamy ten model w praktyce:

  • Ustalenie trybu pracy: Stwierdzamy, czy działa PHP-FPM, mod_php, LiteSpeed, FrankenPHP czy php-cgi i jak zarządza się workerami.
  • Aktualizacja i restart: Instalujemy poprawioną wersję i kontrolowanie restartujemy każdego workera.
  • Kontrola procesów: Na podstawie czasów startu i wersji dowodzimy, że nie pozostał żaden stary proces.
  • Sprawdzenie trybów workera: W długo działających aplikacjach sprawdzamy, czy stan związany z żądaniem jest czysto resetowany.

Częste pytania

Czy PHP uruchamia nowy proces dla każdego żądania?

W wierszu poleceń tak, w pracy webowej zazwyczaj nie. PHP-FPM, Apache z mod_php, LiteSpeed czy FrankenPHP pozwalają jednemu procesowi obsługiwać wiele żądań po kolei. Między żądaniami PHP sprząta stan żądania.

Jaka jest różnica między MINIT a RINIT?

MINIT wykonuje się raz, gdy startuje proces PHP, i zakłada trwałe struktury. RINIT wykonuje się na początku każdego pojedynczego żądania i przygotowuje to, czego potrzebuje tylko to żądanie.

Czy pm.max_requests pomaga przeciw CVE-2026-109619?

php.net nie podaje częstszych restartów workerów jako środka przejściowego. To ustawienie ogranicza tylko, ile żądań worker obsłuży, zanim zostanie zrestartowany. Przeciw luce pomaga aktualizacja z następującym po niej restartem wszystkich workerów.

Czym jest tryb workera we FrankenPHP?

W trybie workera aplikacja uruchamia się raz i pozostaje w pamięci. Oszczędza to czas, ale zmienne statyczne, zmienne globalne i cache zostają zachowane między żądaniami. Według dokumentacji FrankenPHP stan związany z żądaniem trzeba więc świadomie resetować.

Źródła

Kontakt natychmiast

Liczy się każda godzina. Proszę napisać do nas teraz.

Proszę krótko opisać system, wersję PHP i obserwacje. Skontaktujemy się z Państwem, ustalimy sytuację i będziemy pracować zdalnie, aż incydent zostanie zamknięty i udokumentowany.

Natychmiastowa pomoc