Trenutno stanje: PHP2Shell. Dvije kritične PHP ranjivosti (CVSS 9.2) omogućuju zlonamjerni kod bez prijave. Zakrpe od 21. 10. 2026.Što sada učiniti →

Kako PHP obrađuje upload datoteka

Između klika na učitavanje i Vašeg koda u PHP-u postoji cijeli lanac obrade. Ova stranica objašnjava multipart/form-data, privremene datoteke, $_FILES, ograničenja i vezu s PHP2Shellom.

Stanje 5 min čitanjaPHP znanje

Ukratko: Kad preglednik učitava datoteku, šalje POST zahtjev u formatu multipart/form-data, u kojem se polja obrasca i sadržaji datoteka nižu kao zasebni dijelovi. PHP sam rastavlja taj zahtjev, svaki dio s datotekom zapisuje u privremenu datoteku u upload_tmp_dir, unosi podatke u $_FILES i tek tada pokreće Vašu skriptu. Skripta datoteku premješta funkcijom move_uploaded_file(); sve što se ne premjesti, PHP briše na kraju zahtjeva.

Put datoteke od preglednika do skripte

Za donositelje odluka ponajprije je važno jedno: upload nije postupak same aplikacije. Velik dio posla obavlja se u PHP interpreteru prije nego što kod CMS-a ili shopa uopće sudjeluje. Zato pogreške u tom dijelu PHP-a pogađaju svaku web-stranicu, imala ona obrazac za upload ili ne.

Pregled tijeka:

  1. Šalje se obrazac s method="POST" i enctype="multipart/form-data".
  2. Web-poslužitelj prihvaća zahtjev i prosljeđuje ga PHP-u, ovisno o načinu rada putem PHP-FPM-a, mod_php-a ili drugog sučelja.
  3. PHP provjerava ograničenja poput post_max_size i rastavlja sadržaj prema graničnom nizu znakova, takozvanom boundaryju.
  4. Obična polja završavaju u $_POST, dijelovi s datotekama zapisuju se u privremene datoteke i opisuju u $_FILES.
  5. Tek sada se pokreće skripta, provjerava datoteku i premješta je na odredište.
  6. Na kraju zahtjeva PHP briše sve privremene datoteke koje nisu premještene.

multipart/form-data: struktura zahtjeva za upload

Zahtjev za upload je običan tekst s ugrađenim podacima datoteke. Zaglavlje Content-Type navodi boundary, a u sadržaju taj niz znakova razdvaja pojedine dijelove. Svaki dio ima vlastita zaglavlja s nazivom polja i, po potrebi, nazivom datoteke. Jako skraćen, bezopasan primjer:

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

Svi podaci u tom tekstu potječu od pošiljatelja, uključujući naziv datoteke i Content-Type dijela. PHP ih preuzima u $_FILES, ali ne provjerava njihovu vjerodostojnost.

Polje $_FILES

Za polje naziva lebenslauf $_FILES['lebenslauf'] prema PHP priručniku sadrži ove ključeve:

Ključ Sadržaj Pouzdano?
name izvorni naziv datoteke kod pošiljatelja ne
full_path putanja koju je javio preglednik, od PHP-a 8.1.0 ne
type MIME tip prema pregledniku, PHP ga ne provjerava ne
size veličina u bajtovima utvrđuje PHP
tmp_name putanja privremene datoteke na poslužitelju dodjeljuje PHP
error šifra pogreške pri obradi uploada postavlja PHP

Kod polja s uglatim zagradama u nazivu, primjerice bilder[], svaki ključ sadrži polje s jednim unosom po datoteci. Aplikacija tada prolazi, na primjer, kroz $_FILES['bilder']['error'] i istim indeksom pristupa ključevima tmp_name i name.

Obrazac dodatno može poslati skriveno polje MAX_FILE_SIZE ispred polja za datoteku. Prema priručniku to je samo savjet pregledniku koji se lako zaobilazi. Pouzdati se možete isključivo u granice koje provodi poslužitelj: u PHP-u, u web-poslužitelju i u samoj aplikaciji.

Ograničenja: php.ini direktive za uploade

Te direktive upravljaju obradom uploada. Zadane vrijednosti potječu iz PHP priručnika i vrijede ako ih nijedan php.ini ne nadjača.

Direktiva Zadano Promjenjivo u
file_uploads 1 samo sistemska konfiguracija
upload_tmp_dir prazno (privremeni direktorij sustava) samo sistemska konfiguracija
upload_max_filesize 2M php.ini, pool, konfiguracija direktorija
post_max_size 8M php.ini, pool, konfiguracija direktorija
max_file_uploads 20 php.ini, pool, konfiguracija direktorija
enable_post_data_reading 1 php.ini, pool, konfiguracija direktorija

post_max_size mora biti veći od upload_max_filesize, a memory_limit bi prema priručniku u pravilu trebao biti veći od post_max_size. Ako u upload_tmp_dir nije moguće pisati, PHP prelazi na privremeni direktorij sustava. S open_basedir privremeni direktorij mora biti dopušten, inače uploadi ne uspijevaju. Praktične vrijednosti i njihove posljedice opisuje članak Smisleno ograničavanje post_max_size i upload_max_filesize.

Ispravno vrednovati šifre pogrešaka

Polje error govori što se dogodilo pri obradi. Konstante idu od UPLOAD_ERR_OK (0) preko 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);
}

Primjer prikazuje samo mehaniku. Provjeru vrste datoteke, pohranu izvan webrootea i blokiranje izvršavanja skripti opisuje vodič Sigurna izvedba uploada datoteka u PHP-u.

Uploadi i PHP2Shell: što znače prijelazne mjere

PHP2Shell ranjivosti CVE-2026-109618 i CVE-2026-109619 tiču se obrade oko zahtjeva u PHP interpreteru. Zato php.net kao prijelazne mjere navodi, među ostalim, post_max_size = 8M (bez uploada) odnosno 32M (s uploadima), upload_max_filesize ispod toga te session.upload_progress.enabled=0. Te vrijednosti pripadaju u php.ini, FPM pool ili konfiguraciju poslužitelja, a nakon toga svi workeri moraju se ponovno pokrenuti.

Stručni autori dodatno predlažu blokiranje multipart/form-data zahtjeva izvan stvarnih putanja za upload na proxyju ili u WAF-u. To je neslužbena mjera s rizikom da obrasci prestanu raditi; odvagivanje opisuje članak Filtriranje zahtjeva za upload na rubu mreže. Nijedna od tih mjera ne zamjenjuje ažuriranje na PHP 8.5.12, 8.4.27, 8.3.36 ili 8.2.35.

Kako PHP SOS konkretno pomaže?

  • Utvrđujemo koje Vaše web-stranice stvarno trebaju uploade i na kojim putanjama.
  • Postavljamo ograničenja i prijelazne mjere po poolu, ponovno pokrećemo sve workere i testiramo obrasce.
  • Provjeravamo direktorije za upload na izvršne datoteke i blokiramo izvršavanje skripti u okviru ojačavanja poslužitelja.
  • Instaliramo PHP ažuriranje i dokumentiramo stanje zakrpa po web-stranici.

Česta pitanja

Gdje PHP najprije sprema učitane datoteke?

U privremeni direktorij. Prema zadanim postavkama to je privremeni direktorij sustava, a s upload_tmp_dir može se promijeniti. Ako se datoteka u skripti ne premjesti, PHP je briše na kraju zahtjeva.

Smijem li vjerovati polju type u $_FILES?

Ne. MIME tip javlja preglednik, a PHP ga prema priručniku ne provjerava. I name i full_path potječu od pošiljatelja. Stvarnu vrstu datoteke utvrdite na poslužitelju na temelju sadržaja.

Zašto je $_FILES prazan iako je datoteka poslana?

Najčešće je cijeli zahtjev bio veći od post_max_size. Tada PHP potpuno prazni $_POST i $_FILES, bez šifre pogreške u polju. Daljnji uzroci su nedostajući enctype u obrascu ili isključeni file_uploads.

Pomaže li protiv PHP2Shella zabrana uploada u aplikaciji?

Ne. Obrada podataka zahtjeva odvija se u PHP-u prije nego što se izvrši kod aplikacije. Učinkovito je PHP ažuriranje i ponovno pokretanje svih workera; stroža ograničenja samo su prijelazna mjera.

Koja je razlika između upload_max_filesize i post_max_size?

upload_max_filesize ograničava svaku pojedinu datoteku, a post_max_size cjelokupni sadržaj zahtjeva uključujući sve datoteke i polja. post_max_size zato mora biti veći od upload_max_filesize.

Izvori

Hitni kontakt

Svaki sat se računa. Pišite nam odmah.

Ukratko opišite sustav, verziju PHP-a i opažanje. Javit ćemo Vam se, razjasniti situaciju i raditi na daljinu dok incident nije zaključen i dokumentiran.

Hitna pomoć