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ć →

Jak PHP przetwarza przesyłane pliki

Między kliknięciem „Prześlij” a Państwa kodem leży w PHP cały łańcuch przetwarzania. Ta strona wyjaśnia multipart/form-data, pliki tymczasowe, $_FILES, limity i związek z PHP2Shell.

Stan 5 min czytaniaWiedza o PHP

Krótko: Gdy przeglądarka przesyła plik, wysyła żądanie POST w formacie multipart/form-data, w którym pola formularza i zawartość plików występują jako kolejne osobne części. PHP samo rozbija to żądanie, zapisuje każdą część z plikiem w pliku tymczasowym w upload_tmp_dir, wpisuje dane do $_FILES i dopiero wtedy uruchamia Państwa skrypt. Skrypt przenosi plik za pomocą move_uploaded_file(); wszystko, czego nie przeniesie, PHP usuwa na końcu żądania.

Droga pliku od przeglądarki do skryptu

Dla osób podejmujących decyzje ważne jest przede wszystkim jedno: przesyłanie plików nie jest operacją samej aplikacji. Duża część pracy odbywa się w interpreterze PHP, zanim kod CMS lub sklepu w ogóle weźmie w niej udział. Dlatego błędy w tej części PHP dotyczą każdej strony, niezależnie od tego, czy ma formularz przesyłania plików.

Przebieg w skrócie:

  1. Formularz z method="POST" i enctype="multipart/form-data" zostaje wysłany.
  2. Serwer WWW przyjmuje żądanie i przekazuje je do PHP, zależnie od trybu pracy przez PHP-FPM, mod_php lub inny interfejs.
  3. PHP sprawdza limity, takie jak post_max_size, i rozbija zawartość na podstawie ciągu znaków rozdzielającego, czyli tzw. boundary.
  4. Zwykłe pola trafiają do $_POST, części z plikami są zapisywane w plikach tymczasowych i opisywane w $_FILES.
  5. Dopiero teraz zaczyna działać skrypt, sprawdza plik i przenosi go do miejsca docelowego.
  6. Na końcu żądania PHP usuwa wszystkie pliki tymczasowe, które nie zostały przeniesione.

multipart/form-data: budowa żądania z plikiem

Żądanie z plikiem to zwykły tekst z osadzonymi danymi pliku. Nagłówek Content-Type podaje boundary, a w treści ten ciąg znaków oddziela poszczególne części. Każda część ma własne nagłówki z nazwą pola i ewentualnie nazwą pliku. Mocno skrócony, nieszkodliwy przykład:

POST /bewerbung HTTP/1.1
Content-Type: multipart/form-data; boundary=----trenner42

------trenner42
Content-Disposition: form-data; name="vorname"

Erika
------trenner42
Content-Disposition: form-data; name="lebenslauf"; filename="lebenslauf.pdf"
Content-Type: application/pdf

%PDF-1.7 ... Dateiinhalt ...
------trenner42--

Wszystkie dane w tym tekście pochodzą od nadawcy, także nazwa pliku i Content-Type danej części. PHP przejmuje je do $_FILES, ale nie sprawdza ich wiarygodności.

Tablica $_FILES

Dla pola o nazwie lebenslauf tablica $_FILES['lebenslauf'] zawiera według podręcznika PHP następujące klucze:

Klucz Zawartość Wiarygodne?
name pierwotna nazwa pliku u nadawcy nie
full_path ścieżka zgłoszona przez przeglądarkę, od PHP 8.1.0 nie
type typ MIME według przeglądarki, niesprawdzany przez PHP nie
size rozmiar w bajtach ustala PHP
tmp_name ścieżka pliku tymczasowego na serwerze nadaje PHP
error kod błędu przetwarzania przesyłania ustawia PHP

W polach z nawiasami kwadratowymi w nazwie, na przykład bilder[], każdy klucz zawiera tablicę z jednym wpisem na plik. Aplikacja przechodzi wtedy na przykład przez $_FILES['bilder']['error'] i przez ten sam indeks sięga po tmp_name i name.

Formularz może dodatkowo wysłać przed polem pliku ukryte pole MAX_FILE_SIZE. Według podręcznika jest to tylko wskazówka dla przeglądarki, którą łatwo obejść. Polegać można wyłącznie na granicach, które wymusza serwer: w PHP, w serwerze WWW i w samej aplikacji.

Limity: dyrektywy php.ini dla przesyłania plików

Te dyrektywy sterują przetwarzaniem przesyłanych plików. Wartości domyślne pochodzą z podręcznika PHP i obowiązują, gdy żadne php.ini ich nie nadpisuje.

Dyrektywa Domyślnie Zmienialna w
file_uploads 1 tylko konfiguracja systemowa
upload_tmp_dir puste (katalog tymczasowy systemu) tylko konfiguracja systemowa
upload_max_filesize 2M php.ini, pula, konfiguracja katalogu
post_max_size 8M php.ini, pula, konfiguracja katalogu
max_file_uploads 20 php.ini, pula, konfiguracja katalogu
enable_post_data_reading 1 php.ini, pula, konfiguracja katalogu

post_max_size musi być większe niż upload_max_filesize, a memory_limit powinno według podręcznika z reguły być większe niż post_max_size. Jeśli do upload_tmp_dir nie można zapisywać, PHP przechodzi na katalog tymczasowy systemu. Przy open_basedir katalog tymczasowy musi być dozwolony, w przeciwnym razie przesyłanie plików się nie powiedzie. Wartości praktyczne i ich skutki opisuje artykuł Sensowne ograniczanie post_max_size i upload_max_filesize.

Prawidłowa ocena kodów błędów

Pole error mówi, co się stało podczas przetwarzania. Stałe obejmują zakres od UPLOAD_ERR_OK (0) przez UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) i UPLOAD_ERR_NO_FILE (4) do UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) i UPLOAD_ERR_EXTENSION (8).

<?php
declare(strict_types=1);

$datei = $_FILES['lebenslauf'] ?? null;
if ($datei === null) {
    exit('Keine Upload-Daten, eventuell wurde post_max_size überschritten.');
}

$meldung = match ($datei['error']) {
    UPLOAD_ERR_OK => null,
    UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'Datei zu groß.',
    UPLOAD_ERR_NO_FILE => 'Keine Datei gewählt.',
    default => 'Upload fehlgeschlagen.',
};

if ($meldung === null && is_uploaded_file($datei['tmp_name'])) {
    $ziel = '/srv/app/uploads/' . bin2hex(random_bytes(16)) . '.pdf';
    move_uploaded_file($datei['tmp_name'], $ziel);
}

Przykład pokazuje tylko mechanikę. Sprawdzanie typu pliku, zapis poza webrootem i blokowanie wykonywania skryptów opisuje poradnik Bezpieczne przesyłanie plików w PHP.

Przesyłanie plików a PHP2Shell: co oznaczają środki przejściowe

Luki PHP2Shell CVE-2026-109618 i CVE-2026-109619 dotyczą przetwarzania w interpreterze PHP związanego z żądaniem. Dlatego php.net podaje jako środki przejściowe między innymi post_max_size = 8M (bez przesyłania plików) lub 32M (z przesyłaniem plików), upload_max_filesize poniżej tej wartości oraz session.upload_progress.enabled=0. Te wartości należą do php.ini, puli FPM lub konfiguracji serwera, po czym wszystkie workery muszą się zrestartować.

Autorzy branżowi proponują dodatkowo blokowanie żądań multipart/form-data poza rzeczywistymi ścieżkami przesyłania plików na proxy lub w WAF. Jest to środek nieoficjalny, z ryzykiem, że formularze przestaną działać; rozważenie za i przeciw opisuje artykuł Filtrowanie żądań przesyłania plików na brzegu. Żaden z tych środków nie zastępuje aktualizacji do PHP 8.5.12, 8.4.27, 8.3.36 lub 8.2.35.

Jak konkretnie pomaga PHP SOS?

  • Ustalamy, które z Państwa stron rzeczywiście potrzebują przesyłania plików i na jakich ścieżkach.
  • Ustawiamy limity i środki przejściowe dla każdej puli, restartujemy wszystkie workery i testujemy formularze.
  • Sprawdzamy katalogi z przesłanymi plikami pod kątem plików wykonywalnych i blokujemy wykonywanie skryptów w ramach utwardzania serwera.
  • Instalujemy aktualizację PHP i dokumentujemy stan poprawek dla każdej strony.

Częste pytania

Gdzie PHP zapisuje przesłane pliki w pierwszej kolejności?

W katalogu tymczasowym. Domyślnie jest to katalog tymczasowy systemu; można go zmienić przez upload_tmp_dir. Jeśli skrypt nie przeniesie pliku, PHP usuwa go na końcu żądania.

Czy mogę ufać polu type w $_FILES?

Nie. Typ MIME zgłasza przeglądarka, a PHP według podręcznika go nie sprawdza. Także name i full_path pochodzą od nadawcy. Rzeczywisty typ pliku należy ustalić po stronie serwera na podstawie zawartości.

Dlaczego $_FILES jest puste, mimo że plik został wysłany?

Zwykle całe żądanie było większe niż post_max_size. Wtedy PHP całkowicie opróżnia $_POST i $_FILES, bez kodu błędu w tablicy. Inne przyczyny to brak enctype w formularzu lub wyłączone file_uploads.

Czy zakaz przesyłania plików w aplikacji pomaga przeciw PHP2Shell?

Nie. Przetwarzanie danych żądania odbywa się w PHP, zanim zacznie działać kod aplikacji. Skuteczne są aktualizacja PHP i restart wszystkich workerów; węższe limity to tylko środek przejściowy.

Jaka jest różnica między upload_max_filesize a post_max_size?

upload_max_filesize ogranicza każdy pojedynczy plik, a post_max_size całą zawartość żądania, łącznie ze wszystkimi plikami i polami. Dlatego post_max_size musi być większe niż upload_max_filesize.

Ź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