Dabartinė padėtis: PHP2Shell. Du kritiniai PHP pažeidžiamumai (CVSS 9,2) leidžia vykdyti kenkėjišką kodą be prisijungimo. Pataisos nuo 2026-10-21.Ką dabar daryti →

Kaip PHP apdoroja failų įkėlimus

Tarp paspaudimo „Įkelti“ ir jūsų kodo PHP vyksta ištisa apdorojimo grandinė. Šiame puslapyje paaiškinti multipart/form-data, laikini failai, $_FILES, ribinės reikšmės ir ryšys su PHP2Shell.

Būsena 4 min. skaitymoPHP žinios

Trumpai: Kai naršyklė įkelia failą, ji siunčia POST užklausą formatu multipart/form-data, kurioje formos laukai ir failų turinys eina vienas po kito kaip atskiros dalys. PHP pats išskaido šią užklausą, kiekvieną failo dalį įrašo į laikiną failą kataloge upload_tmp_dir, įrašo duomenis į $_FILES ir tik tada paleidžia jūsų skriptą. Skriptas perkelia failą su move_uploaded_file(); viską, kas neperkelta, PHP ištrina užklausos pabaigoje.

Failo kelias nuo naršyklės iki skripto

Sprendimų priėmėjams svarbiausia viena: įkėlimas nėra vien programos veiksmas. Didelė darbo dalis atliekama PHP interpretatoriuje dar prieš įsitraukiant CMS ar parduotuvės kodui. Todėl šios PHP dalies klaidos veikia kiekvieną svetainę, nesvarbu, ar ji turi įkėlimo formą, ar ne.

Eiga trumpai:

  1. Išsiunčiama forma su method="POST" ir enctype="multipart/form-data".
  2. Žiniatinklio serveris priima užklausą ir perduoda ją PHP, priklausomai nuo veikimo režimo per PHP-FPM, mod_php ar kitą sąsają.
  3. PHP patikrina ribines reikšmes, tokias kaip post_max_size, ir išskaido turinį pagal skiriamąją eilutę, vadinamą boundary.
  4. Įprasti laukai patenka į $_POST, failų dalys įrašomos į laikinus failus ir aprašomos $_FILES.
  5. Tik dabar paleidžiamas skriptas, patikrina failą ir perkelia jį į paskirties vietą.
  6. Užklausos pabaigoje PHP ištrina visus laikinus failus, kurie nebuvo perkelti.

multipart/form-data: įkėlimo užklausos sandara

Įkėlimo užklausa yra grynas tekstas su įterptais failo duomenimis. Antraštė Content-Type nurodo boundary, o turinyje ši eilutė atskiria atskiras dalis. Kiekviena dalis turi savo antraštes su lauko pavadinimu ir, jei reikia, failo pavadinimu. Stipriai sutrumpintas nekenksmingas pavyzdys:

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

Visi šio teksto duomenys ateina iš siuntėjo, taip pat failo pavadinimas ir dalies Content-Type. PHP juos perima į $_FILES, bet neįvertina, ar jie tikėtini.

Masyvas $_FILES

Laukui, pavadintam lebenslauf, $_FILES['lebenslauf'], pasak PHP vadovo, turi šiuos raktus:

Raktas Turinys Ar galima pasitikėti?
name pradinis failo pavadinimas pas siuntėją ne
full_path naršyklės pranešamas kelias, nuo PHP 8.1.0 ne
type MIME tipas pagal naršyklę, PHP netikrinamas ne
size dydis baitais nustato PHP
tmp_name laikino failo kelias serveryje suteikia PHP
error įkėlimo apdorojimo klaidos kodas nustato PHP

Laukams su laužtiniais skliaustais pavadinime, pavyzdžiui, bilder[], kiekvienas raktas turi masyvą su vienu įrašu kiekvienam failui. Programa tuomet pereina, pavyzdžiui, per $_FILES['bilder']['error'] ir tuo pačiu indeksu pasiekia tmp_name ir name.

Forma papildomai prieš failo lauką gali siųsti paslėptą lauką MAX_FILE_SIZE. Pasak vadovo, tai tik užuomina naršyklei, kurią lengva apeiti. Galite pasikliauti tik ribomis, kurias įgyvendina serveris: PHP, žiniatinklio serveryje ir pačioje programoje.

Ribinės reikšmės: php.ini direktyvos įkėlimams

Šios direktyvos valdo įkėlimų apdorojimą. Numatytosios reikšmės paimtos iš PHP vadovo ir galioja, kai jų nepaneigia php.ini.

Direktyva Numatytoji Keičiama kur
file_uploads 1 tik sistemos konfigūracijoje
upload_tmp_dir tuščia (sistemos temp) tik sistemos konfigūracijoje
upload_max_filesize 2M php.ini, baseine (pool), katalogo konfigūracijoje
post_max_size 8M php.ini, baseine (pool), katalogo konfigūracijoje
max_file_uploads 20 php.ini, baseine (pool), katalogo konfigūracijoje
enable_post_data_reading 1 php.ini, baseine (pool), katalogo konfigūracijoje

post_max_size turi būti didesnis nei upload_max_filesize, o memory_limit, pasak vadovo, paprastai turėtų būti didesnis nei post_max_size. Jei į upload_tmp_dir rašyti negalima, PHP naudoja sistemos temp katalogą. Su open_basedir temp katalogas turi būti leistinas, antraip įkėlimai nepavyksta. Praktines reikšmes ir jų pasekmes aprašo straipsnis Kaip prasmingai apriboti post_max_size ir upload_max_filesize.

Teisingas klaidų kodų vertinimas

Laukas error nurodo, kas įvyko apdorojant. Konstantos eina nuo UPLOAD_ERR_OK (0) per UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) ir UPLOAD_ERR_NO_FILE (4) iki UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) ir 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);
}

Pavyzdys rodo tik mechaniką. Failo tipo patikrą, saugojimą už žiniatinklio šakninio katalogo ribų ir skriptų vykdymo blokavimą aprašo vadovas Saugus failų įkėlimas PHP.

Įkėlimai ir PHP2Shell: ką reiškia pereinamosios priemonės

PHP2Shell spragos CVE-2026-109618 ir CVE-2026-109619 liečia užklausos apdorojimą PHP interpretatoriuje. Todėl php.net kaip pereinamąsias priemones nurodo, be kita ko, post_max_size = 8M (be įkėlimų) arba 32M (su įkėlimais), mažesnį upload_max_filesize ir session.upload_progress.enabled=0. Šios reikšmės turi būti php.ini, FPM baseine arba serverio konfigūracijoje, o po to visi darbiniai procesai (worker) turi būti paleisti iš naujo.

Specialistai papildomai siūlo multipart/form-data užklausas už tikrųjų įkėlimo kelių blokuoti tarpiniame serveryje (proxy) arba WAF. Tai neoficiali priemonė, kuri gali sugadinti formas; svarstymus aprašo straipsnis Įkėlimo užklausų filtravimas ties riba. Nė viena iš šių priemonių neatstoja atnaujinimo iki PHP 8.5.12, 8.4.27, 8.3.36 ar 8.2.35.

Kaip PHP SOS padeda konkrečiai?

  • Nustatome, kurioms jūsų svetainėms įkėlimai iš tikrųjų reikalingi ir kokiais keliais.
  • Nustatome ribines reikšmes ir pereinamąsias priemones kiekvienam baseinui (pool), paleidžiame iš naujo visus darbinius procesus (worker) ir išbandome formas.
  • Tikriname įkėlimų katalogus dėl vykdomų failų ir blokuojame skriptų vykdymą pagal serverio sutvirtinimo paslaugą.
  • Įdiegiame PHP atnaujinimą ir dokumentuojame pataisų būseną kiekvienai svetainei.

Dažni klausimai

Kur PHP pirmiausia saugo įkeltus failus?

Laikinajame kataloge. Pagal numatymą tai sistemos laikinasis katalogas; jį galima pakeisti per upload_tmp_dir. Jei skripte failas neperkeliamas, PHP jį ištrina užklausos pabaigoje.

Ar galiu pasitikėti laukeliu type masyve $_FILES?

Ne. MIME tipą praneša naršyklė, o PHP, pasak vadovo, jo netikrina. Taip pat name ir full_path ateina iš siuntėjo. Tikrąjį failo tipą nustatykite serverio pusėje pagal turinį.

Kodėl $_FILES tuščias, nors failas buvo išsiųstas?

Dažniausiai visa užklausa buvo didesnė nei post_max_size. Tuomet PHP visiškai ištuština $_POST ir $_FILES, o masyve klaidos kodo nėra. Kitos priežastys: formoje trūksta enctype arba išjungtas file_uploads.

Ar nuo PHP2Shell padeda uždrausti įkėlimus programoje?

Ne. Užklausos duomenys apdorojami PHP viduje, dar prieš pradedant vykdyti programos kodą. Veiksmingas yra PHP atnaujinimas ir visų darbinių procesų (worker) paleidimas iš naujo; griežtesnės ribinės reikšmės yra tik pereinamoji priemonė.

Kuo skiriasi upload_max_filesize ir post_max_size?

upload_max_filesize riboja kiekvieną atskirą failą, o post_max_size visą užklausos turinį, įskaitant visus failus ir laukus. Todėl post_max_size turi būti didesnis nei upload_max_filesize.

Šaltiniai

Skubus kontaktas

Kiekviena valanda svarbi. Parašykite mums dabar.

Trumpai aprašykite sistemą, PHP versiją ir pastebėjimus. Mes atsakysime, išsiaiškinsime padėtį ir dirbsime nuotoliu, kol incidentas bus užbaigtas ir dokumentuotas.

Skubi pagalba