<?> PHP NOVA invictum.hr
Izdanje 2026.
Poglavlje 24

Upload datoteka

Izrađujemo upload koji ne vjeruje nazivu, ekstenziji ni sadržaju datoteke te ograničava štetu ako provjera ipak zakaže.

27 min čitanja Napredno razina PHP 8.5 ciljna verzija

Nakon ovog poglavlja

Što ćete znati napraviti

  • Provjeriti PHP upload kod, veličinu i stvarni MIME tip.
  • Generirati vlastiti naziv i spremiti datoteku izvan public mape.
  • Primijeniti allowlist, ograničenja i sigurno posluživanje.

Poseban oblik zahtjeva

multipart/form-data i $_FILES

Forma mora imati enctype multipart/form-data. PHP podatke daje u $_FILES, uključujući privremenu putanju i upload error kod. Prvo provjeravamo UPLOAD_ERR_OK; veličina iz preglednika nije pouzdana pa primjenjujemo poslužiteljsko ograničenje.

Upload forma
<form method="post" enctype="multipart/form-data">
  <label for="avatar">Profilna slika</label>
  <input id="avatar" name="avatar" type="file" accept="image/png,image/jpeg">
  <button type="submit">Učitaj</button>
</form>

Ne vjerujemo metapodacima klijenta

Allowlist MIME tipova i veličine

Originalni naziv služi samo za eventualni prikaz i mora se escapati. Ekstenziju biramo prema dopuštenom MIME tipu koji očita Fileinfo. Za slike je sigurnije dekodirati i ponovno kodirati sadržaj provjerenom bibliotekom, čime odbacujemo mnoge skrivene dodatke.

Server-side provjera
<?php

$file = $_FILES['avatar'] ?? null;

if (!is_array($file) || $file['error'] !== UPLOAD_ERR_OK) {
    throw new RuntimeException('Upload nije uspio.');
}

if ($file['size'] > 2_000_000) {
    throw new RuntimeException('Datoteka je prevelika.');
}

$mime = (new finfo(FILEINFO_MIME_TYPE))->file($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png'];

if (!isset($allowed[$mime])) {
    throw new RuntimeException('Vrsta datoteke nije dopuštena.');
}

Smanjenje posljedica

Vlastiti naziv, mapa bez izvršavanja

Generiramo nepredvidiv naziv, primjerice random_bytes, i spremamo izvan javnog document roota. Datoteku poslužujemo kontroliranim endpointom uz Content-Type, Content-Disposition i autorizaciju. Ako mora biti javna, mapa ne smije izvršavati skripte.

move_uploaded_file provjerava da je izvor stvarno PHP upload. Prije spremanja osigurajte ograničene dozvole, raspoloživ prostor, kvotu po korisniku i zaštitu od prevelikog broja datoteka.

Sigurno spremanje
<?php

$filename = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
$target = dirname(__DIR__) . '/storage/uploads/' . $filename;

if (!move_uploaded_file($file['tmp_name'], $target)) {
    throw new RuntimeException('Datoteku nije moguće spremiti.');
}

Primijenite naučeno

Vježba: Siguran avatar

Izradite upload JPEG ili PNG avatara do 2 MB.

  1. Provjerite upload error, veličinu i MIME kroz Fileinfo.
  2. Generirajte vlastiti nasumični naziv.
  3. Spremite datoteku izvan public mape i zabilježite samo naziv.

Za provjeru i nastavak

Službeni izvori