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.
<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.
<?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.
<?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.
- Provjerite upload error, veličinu i MIME kroz Fileinfo.
- Generirajte vlastiti nasumični naziv.
- Spremite datoteku izvan public mape i zabilježite samo naziv.
Za provjeru i nastavak