Röviden: Amikor egy böngésző fájlt tölt fel, multipart/form-data formátumú POST-kérést küld, amelyben az űrlapmezők és a fájltartalmak külön részekként követik egymást. A PHP ezt a kérést maga bontja szét, minden fájlrészt ideiglenes fájlba ír az upload_tmp_dir könyvtárban, bejegyzi az adatokat a $_FILES-ba, és csak ezután indítja el az Ön szkriptjét. A szkript a move_uploaded_file() függvénnyel helyezi át a fájlt; amit nem helyez át, azt a PHP a kérés végén törli.
A fájl útja a böngészőtől a szkriptig
A döntéshozók számára elsősorban egy dolog fontos: a feltöltés nem pusztán az alkalmazás ügye. A munka nagy része a PHP-értelmezőben zajlik, még mielőtt a CMS vagy a webshop kódja egyáltalán részt venne benne. Ezért a PHP ezen részének hibái minden weboldalt érintenek, akár van feltöltési űrlapja, akár nincs.
A folyamat áttekintése:
- Elküldik az űrlapot
method="POST"ésenctype="multipart/form-data"beállítással. - A webszerver elfogadja a kérést, és továbbadja a PHP-nak, az üzemmódtól függően PHP-FPM-en, mod_php-n vagy más felületen keresztül.
- A PHP ellenőrzi a korlátokat, például a
post_max_size-t, és egy elválasztó karaktersorozat, az úgynevezett boundary mentén felbontja a tartalmat. - A normál mezők a
$_POST-ba kerülnek, a fájlrészeket ideiglenes fájlokba írja a PHP, és leírja őket a$_FILES-ban. - Csak most indul el a szkript, ellenőrzi a fájlt, és a célhelyére helyezi.
- A kérés végén a PHP törli az összes ideiglenes fájlt, amelyet nem helyeztek át.
multipart/form-data: a feltöltési kérés felépítése
A feltöltési kérés tiszta szöveg, beágyazott fájladatokkal. A Content-Type fejléc megadja a boundary-t, a tartalomban pedig ez a karaktersorozat választja el egymástól az egyes részeket. Minden résznek saját fejlécsorai vannak a mezőnévvel és adott esetben a fájlnévvel. Egy erősen lerövidített, ártalmatlan példa:
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--
Ebben a szövegben minden adat a feladótól származik, a fájlnév és a rész Content-Type-ja is. A PHP átveszi őket a $_FILES-ba, de hihetőségüket nem ellenőrzi.
A $_FILES tömb
A lebenslauf nevű mező esetén a $_FILES['lebenslauf'] a PHP-kézikönyv szerint ezeket a kulcsokat tartalmazza:
| Kulcs | Tartalom | Megbízható? |
|---|---|---|
name |
a fájl eredeti neve a feladónál | nem |
full_path |
a böngésző által jelentett útvonal, a PHP 8.1.0 óta | nem |
type |
a böngésző szerinti MIME-típus, a PHP nem ellenőrzi | nem |
size |
méret bájtban | a PHP állapítja meg |
tmp_name |
az ideiglenes fájl útvonala a szerveren | a PHP adja |
error |
a feltöltés-feldolgozás hibakódja | a PHP állítja be |
Szögletes zárójeles nevű mezőknél, például bilder[], minden kulcs egy tömböt tartalmaz fájlonként egy bejegyzéssel. Az alkalmazás ilyenkor például a $_FILES['bilder']['error']-on megy végig, és ugyanazzal az indexszel éri el a tmp_name-et és a name-et.
Az űrlap emellett küldhet a fájlmező előtt egy rejtett MAX_FILE_SIZE mezőt. A kézikönyv szerint ez csupán tanács a böngészőnek, amely könnyen megkerülhető. Kizárólag azokra a korlátokra támaszkodhat, amelyeket a szerver érvényesít: a PHP-ban, a webszerverben és magában az alkalmazásban.
Korlátok: a feltöltések php.ini-direktívái
Ezek a direktívák vezérlik a feltöltések feldolgozását. Az alapértékek a PHP-kézikönyvből származnak, és akkor érvényesek, ha egy php.ini nem írja felül őket.
| Direktíva | Alapérték | Módosítható itt |
|---|---|---|
file_uploads |
1 |
csak rendszerkonfiguráció |
upload_tmp_dir |
üres (rendszer ideiglenes könyvtára) | csak rendszerkonfiguráció |
upload_max_filesize |
2M |
php.ini, pool, könyvtárkonfiguráció |
post_max_size |
8M |
php.ini, pool, könyvtárkonfiguráció |
max_file_uploads |
20 |
php.ini, pool, könyvtárkonfiguráció |
enable_post_data_reading |
1 |
php.ini, pool, könyvtárkonfiguráció |
A post_max_size-nak nagyobbnak kell lennie az upload_max_filesize-nál, a memory_limit-nek pedig a kézikönyv szerint általában nagyobbnak kell lennie a post_max_size-nál. Ha az upload_tmp_dir nem írható, a PHP a rendszer ideiglenes könyvtárára tér át. Az open_basedir használatakor az ideiglenes könyvtárnak engedélyezettnek kell lennie, különben a feltöltések meghiúsulnak. A gyakorlati értékeket és következményeiket a post_max_size és upload_max_filesize ésszerű korlátozása cikk írja le.
A hibakódok helyes kiértékelése
Az error mező megmondja, mi történt a feldolgozás során. A konstansok az UPLOAD_ERR_OK (0) értéktől az UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) és UPLOAD_ERR_NO_FILE (4) értéken át az UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) és UPLOAD_ERR_EXTENSION (8) értékig terjednek.
<?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);
}
A példa csak a működési mechanizmust mutatja. A fájltípus ellenőrzését, a webgyökéren kívüli tárolást és a szkriptfuttatás tiltását a Fájlfeltöltések biztonságos megvalósítása PHP-ban útmutató írja le.
Feltöltések és PHP2Shell: mit jelentenek az átmeneti intézkedések
A CVE-2026-109618 és CVE-2026-109619 számú PHP2Shell-rések a kérés körüli feldolgozást érintik a PHP-értelmezőben. Ezért a php.net átmeneti intézkedésként többek között a post_max_size = 8M (feltöltések nélkül) vagy 32M (feltöltésekkel) értéket, az upload_max_filesize ennél alacsonyabb értékét, valamint a session.upload_progress.enabled=0 beállítást nevezi meg. Ezeket az értékeket a php.ini-be, az FPM-poolba vagy a szerverkonfigurációba kell írni, utána pedig minden workert újra kell indítani.
Szakmai szerzők emellett azt javasolják, hogy a valódi feltöltési útvonalakon kívüli multipart/form-data kéréseket a proxyn vagy a WAF-on blokkolják. Ez nem hivatalos intézkedés, és fennáll a kockázata, hogy az űrlapok elromlanak; a mérlegelést az Upload-kérések szűrése a peremen cikk írja le. Ezen intézkedések egyike sem helyettesíti a frissítést a PHP 8.5.12, 8.4.27, 8.3.36 vagy 8.2.35 verzióra.
Hogyan segít konkrétan a PHP SOS?
- Megállapítjuk, hogy az Ön weboldalai közül melyeknek van ténylegesen szükségük feltöltésekre, és mely útvonalakon.
- Poolonként beállítjuk a korlátokat és az átmeneti intézkedéseket, újraindítjuk az összes workert, és teszteljük az űrlapokat.
- Ellenőrizzük a feltöltési könyvtárakat futtatható fájlok szempontjából, és a szerverkeményítés keretében letiltjuk a szkriptfuttatást.
- Telepítjük a PHP-frissítést, és weboldalanként dokumentáljuk a javítási állapotot.
Gyakori kérdések
Hová menti a PHP elsőként a feltöltött fájlokat?
Egy ideiglenes könyvtárba. Alapértelmezés szerint ez a rendszer ideiglenes könyvtára, amely az upload_tmp_dir-rel módosítható. Ha a szkript nem helyezi át a fájlt, a PHP a kérés végén törli.
Megbízhatok a $_FILES type mezőjében?
Nem. Az MIME-típust a böngésző jelenti, a PHP a kézikönyv szerint nem ellenőrzi. A name és a full_path is a feladótól származik. A tényleges fájltípust a szerveroldalon, a tartalom alapján állapítsa meg.
Miért üres a $_FILES, pedig küldtek fájlt?
Többnyire a teljes kérés nagyobb volt a post_max_size-nál. Ilyenkor a PHP teljesen kiüríti a $_POST-ot és a $_FILES-t, a tömbben hibakód nélkül. További okok: hiányzó enctype az űrlapban vagy kikapcsolt file_uploads.
Segít a PHP2Shell ellen, ha az alkalmazásban megtiltom a feltöltéseket?
Nem. A kéradatok feldolgozása a PHP-ban történik, még az alkalmazáskód futása előtt. A PHP-frissítés és az összes worker újraindítása hatásos; a szűkebb korlátok csak átmeneti intézkedések.
Mi a különbség az upload_max_filesize és a post_max_size között?
Az upload_max_filesize az egyes fájlokat korlátozza, a post_max_size a kérés teljes tartalmát az összes fájllal és mezővel együtt. A post_max_size-nak ezért nagyobbnak kell lennie az upload_max_filesize-nál.