Lühidalt: Kui brauser faili üles laadib, saadab ta POST-päringu vormingus multipart/form-data, milles vormiväljad ja failide sisu on üksteise järel eraldi osadena. PHP lahutab selle päringu ise osadeks, kirjutab iga faili osa ajutisse faili kataloogi upload_tmp_dir, kannab andmed massiivi $_FILES ja käivitab alles seejärel teie skripti. Skript teisaldab faili funktsiooniga move_uploaded_file(); kõik, mida ei teisaldata, kustutab PHP päringu lõpus.
Faili tee brauserist skriptini
Otsustajale on kõige olulisem üks asi: üleslaadimine ei ole üksnes rakenduse tegevus. Suur osa tööst toimub PHP interpretaatoris, enne kui CMS-i või poe kood üldse kaasa tuleb. Seetõttu mõjutavad selle PHP osa vead iga veebisaiti, olenemata sellest, kas sel on üleslaadimisvorm või mitte.
Kulg lühidalt:
- Vorm, milles on
method="POST"jaenctype="multipart/form-data", saadetakse ära. - Veebiserver võtab päringu vastu ja annab selle edasi PHP-le, olenevalt töörežiimist PHP-FPM-i, mod_php või mõne muu liidese kaudu.
- PHP kontrollib piiranguid, näiteks
post_max_size, ja jaotab sisu osadeks eraldusmärgijada, nn boundary, järgi. - Tavalised väljad satuvad massiivi
$_POST, failiosad kirjutatakse ajutistesse failidesse ja kirjeldatakse massiivis$_FILES. - Alles nüüd käivitub skript, kontrollib faili ja teisaldab selle sihtkohta.
- Päringu lõpus kustutab PHP kõik ajutised failid, mida ei teisaldatud.
multipart/form-data: üleslaadimispäringu ülesehitus
Üleslaadimispäring on puhas tekst, millesse on põimitud failiandmed. Päis Content-Type nimetab boundary, ja sisus eraldab see märgijada üksikud osad. Igal osal on oma päisread välja nime ja vajaduse korral failinimega. Tugevalt lühendatud, kahjutu näide:
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--
Kõik selle teksti andmed pärinevad saatjalt, ka failinimi ja osa Content-Type. PHP võtab need üle massiivi $_FILES, kuid ei kontrolli nende usutavust.
Massiiv $_FILES
Välja nimega lebenslauf korral sisaldab $_FILES['lebenslauf'] PHP käsiraamatu järgi järgmisi võtmeid:
| Võti | Sisu | Usaldusväärne? |
|---|---|---|
name |
algne failinimi saatja juures | ei |
full_path |
brauseri teatatud tee, alates PHP 8.1.0-st | ei |
type |
MIME-tüüp brauseri järgi, PHP ei kontrolli seda | ei |
size |
suurus baitides | PHP arvutab |
tmp_name |
ajutise faili tee serveris | PHP annab |
error |
üleslaadimise töötluse veakood | PHP seab |
Väljade korral, mille nimes on nurksulud, näiteks bilder[], sisaldab iga võti massiivi, kus on iga faili kohta üks kirje. Rakendus käib siis läbi näiteks $_FILES['bilder']['error'] ja pöördub sama indeksi kaudu tmp_name ja name poole.
Vorm võib enne failivälja saata ka peidetud välja MAX_FILE_SIZE. Käsiraamatu järgi on see üksnes vihje brauserile, mida on lihtne mööda minna. Toetuda saate ainult piirangutele, mida server jõustab: PHP-s, veebiserveris ja rakenduses endas.
Piirangud: php.ini direktiivid üleslaadimiste jaoks
Need direktiivid juhivad üleslaadimiste töötlemist. Vaikeväärtused pärinevad PHP käsiraamatust ja kehtivad, kui php.ini neid üle ei kirjuta.
| Direktiiv | Vaikeväärtus | Muudetav kohas |
|---|---|---|
file_uploads |
1 |
ainult süsteemi seadistuses |
upload_tmp_dir |
tühi (süsteemi ajutine kataloog) | ainult süsteemi seadistuses |
upload_max_filesize |
2M |
php.ini, pool, kataloogi seadistus |
post_max_size |
8M |
php.ini, pool, kataloogi seadistus |
max_file_uploads |
20 |
php.ini, pool, kataloogi seadistus |
enable_post_data_reading |
1 |
php.ini, pool, kataloogi seadistus |
post_max_size peab olema suurem kui upload_max_filesize ja memory_limit peaks käsiraamatu järgi tavaliselt olema suurem kui post_max_size. Kui upload_tmp_dir ei ole kirjutatav, kasutab PHP selle asemel süsteemi ajutist kataloogi. Sätte open_basedir korral peab ajutine kataloog olema lubatud, muidu üleslaadimised ebaõnnestuvad. Praktilisi väärtusi ja nende tagajärgi kirjeldab artikkel post_max_size ja upload_max_filesize mõistlik piiramine.
Veakoodide õige hindamine
Väli error ütleb, mis töötluse käigus juhtus. Konstandid ulatuvad väärtusest UPLOAD_ERR_OK (0) läbi UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) ja UPLOAD_ERR_NO_FILE (4) kuni UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) ja 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);
}
Näide näitab ainult mehhanismi. Failitüübi kontrolli, salvestamist väljaspool veebijuurt ja skriptide käivitamise keelamist kirjeldab juhend Failide üleslaadimine PHP-s turvaliselt.
Üleslaadimised ja PHP2Shell: mida ajutised meetmed tähendavad
PHP2Shelli turvaaugud CVE-2026-109618 ja CVE-2026-109619 puudutavad päringu ümber toimuvat töötlust PHP interpretaatoris. Seetõttu nimetab php.net ajutiste meetmetena muu hulgas post_max_size = 8M (ilma üleslaadimisteta) või 32M (üleslaadimistega), upload_max_filesize sellest väiksemana ning session.upload_progress.enabled=0. Need väärtused kuuluvad php.ini-sse, FPM-poolile või serveri seadistusse, pärast seda tuleb kõik workerid uuesti käivitada.
Erialaautorid soovitavad lisaks blokeerida multipart/form-data päringud väljaspool tegelikke üleslaadimisteid puhverserveris või WAF-is. See on mitteametlik meede, mille risk on vormide katkemine; kaalumist kirjeldab artikkel Üleslaadimispäringute filtreerimine serveri serval. Ükski neist meetmetest ei asenda uuendust versioonile PHP 8.5.12, 8.4.27, 8.3.36 või 8.2.35.
Kuidas PHP SOS konkreetselt aitab?
- Selgitame välja, millised teie veebisaidid üleslaadimisi tegelikult vajavad ja millistel teedel.
- Seame piirangud ja ajutised meetmed pooli kaupa, käivitame kõik workerid uuesti ja testime vorme.
- Kontrollime üleslaadimiskatalooge käivitatavate failide suhtes ja keelame skriptide käivitamise serveri karastamise raames.
- Paigaldame PHP uuenduse ja dokumenteerime paigatud seisu iga veebisaidi kohta.
Korduma kippuvad küsimused
Kuhu PHP üleslaaditud failid esmalt salvestab?
Ajutisse kataloogi. Vaikimisi on selleks süsteemi ajutine kataloog; seda saab muuta sättega upload_tmp_dir. Kui skript faili ei teisalda, kustutab PHP selle päringu lõpus.
Kas ma võin usaldada välja type massiivis $_FILES?
Ei. MIME-tüübi teatab brauser ja PHP ei kontrolli seda käsiraamatu järgi. Ka name ja full_path pärinevad saatjalt. Faili tegeliku tüübi tuvastate serveri poolel sisu põhjal.
Miks on $_FILES tühi, kuigi fail saadeti?
Enamasti oli kogu päring suurem kui post_max_size. Siis tühjendab PHP $_POST ja $_FILES täielikult, massiivi veakoodi jätmata. Muud põhjused on vormis puuduv enctype või väljalülitatud file_uploads.
Kas aitab PHP2Shelli vastu üleslaadimiste keelamine rakenduses?
Ei. Päringuandmete töötlemine toimub PHP-s enne, kui rakenduse kood üldse käivitub. Tõhusad on PHP uuendus ja kõigi workerite taaskäivitus; kitsamad piirangud on vaid ajutine meede.
Mis vahe on upload_max_filesize'il ja post_max_size'il?
upload_max_filesize piirab iga üksikut faili, post_max_size kogu päringu sisu koos kõigi failide ja väljadega. post_max_size peab seetõttu olema suurem kui upload_max_filesize.