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 →

Sigurna provedba učitavanja datoteka u PHP-u: sigurnost PHP uploada

Obrasci za upload čest su put za webshellove. Kako ispravno provjeriti vrste datoteka, sigurno ih pohraniti i spriječiti da poslužitelj izvršava učitane skripte.

Stanje 5 min čitanjaSavjetnik

Ukratko: Kod sigurnosti PHP uploada ne odlučuje jedna provjera, nego kombinacija: provjeriti vrstu datoteke na poslužitelju, preimenovati datoteke, pohraniti ih izvan webroota i u mapi za upload ne dopustiti izvršavanje skripti. Neprovjereni obrazac za upload jedan je od klasičnih puteva kojima napadači unose stražnja vrata na web-poslužitelj.

Zašto su obrasci za upload omiljeni ulaz

Svaki obrazac koji prima datoteke upisuje tuđe podatke na Vaš poslužitelj. Tu spadaju obrasci za prijave za posao, portali za klijente, galerije slika, područja podrške i mnoga proširenja CMS-ova i shopova. Ako napadač tamo uspije postaviti datoteku koju web-poslužitelj potom izvrši kao PHP, ima trajna stražnja vrata.

Za upravu to znači: naizgled bezazlen upload životopisa u najgorem slučaju može dovesti do preuzimanja cijelog web-poslužitelja. Osim toga u mapama za upload često su osobni podaci koji su kod pogrešne konfiguracije javno dostupni.

Za admine i programere to znači: aplikacijski kod i konfiguracija poslužitelja moraju se uskladiti. Slabost na jednoj razini često hvata druga ako su obje uredno provedene. Kako pronaći već ubačene datoteke, opisuje vodič Prepoznavanje i uklanjanje webshellova.

Osnovna pravila za sigurnost PHP uploada

Sljedeća pravila odgovaraju preporukama OWASP Cheat Sheetova i pokazala su se dobrima u radu:

  • Popis dopuštenog umjesto popisa zabranjenog: Prihvaćati samo izričito dopuštene vrste datoteka, primjerice PDF, JPG i PNG. Popisi zabrane za pojedine nastavke mogu se zaobići varijantama i dvostrukim nastavcima.
  • Provjeriti sadržaj, a ne samo nastavak: Stvarnu vrstu odrediti na poslužitelju s finfo. MIME tip u $_FILES podatak je pošiljatelja i nije pouzdan.
  • Novo ime datoteke: Izvorno ime nikad ne koristiti kao ime datoteke. Generirati nasumično ime i dodati nastavak s popisa dopuštenog.
  • Pohrana izvan webroota: Datoteke pohraniti tako da nisu izravno dostupne putem URL-a. Isporuka ide preko skripte s provjerom ovlasti.
  • Ograničiti veličinu: U aplikaciji, u PHP-u i u web-poslužitelju.
  • Bez izvršavanja: Ako uploadi ipak moraju biti u webrootu, isključiti izvršavanje skripti u tom direktoriju.
  • Provjera samo na poslužitelju: JavaScript provjere u pregledniku poboljšavaju upotrebljivost, ali ne štite.

Siguran primjer s finfo i nasumičnim imenom

Sljedeći primjer prikazuje srž sigurne obrade. Namjerno je kratak i treba ga ugraditi u Vašu aplikaciju, dakle s prijavom, provjerom ovlasti, CSRF zaštitom i bilježenjem.

<?php
declare(strict_types=1);

const MAX_BYTES  = 5 * 1024 * 1024; // 5 MB
const ERLAUBT    = [
    'application/pdf' => 'pdf',
    'image/jpeg'      => 'jpg',
    'image/png'       => 'png',
];
const ZIELORDNER = '/var/www/daten/uploads'; // izvan webroota

$datei = $_FILES['dokument'] ?? null;

if (!is_array($datei) || $datei['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    exit('Upload fehlgeschlagen.');
}

if ($datei['size'] > MAX_BYTES || !is_uploaded_file($datei['tmp_name'])) {
    http_response_code(400);
    exit('Datei ungültig.');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$typ   = $finfo->file($datei['tmp_name']);

if (!isset(ERLAUBT[$typ])) {
    http_response_code(415);
    exit('Dateityp nicht erlaubt.');
}

$name = bin2hex(random_bytes(16)) . '.' . ERLAUBT[$typ];
$ziel = ZIELORDNER . '/' . $name;

if (!move_uploaded_file($datei['tmp_name'], $ziel)) {
    http_response_code(500);
    exit('Speichern fehlgeschlagen.');
}

chmod($ziel, 0640);
// Izvorno ime pohraniti samo kao tekst u bazi podataka
// i pri svakom ispisu escapirati s htmlspecialchars().

Kod slika isplati se dodatni korak: sliku ponovno kodirati s GD-om ili Imagickom i pritom odbaciti metapodatke. To ne zamjenjuje druge provjere, ali smanjuje preostali rizik zbog ugrađenih tuđih podataka.

Za dokumente iz nepoznatih izvora, primjerice prijave za posao ili fotografije reklamacija, dodatno je smislen antivirusni sken prije prosljeđivanja. On manje štiti web-poslužitelj, a više zaposlenike koji kasnije otvaraju datoteke na svojim radnim računalima. Uz to bilježite svaki upload s vremenom, računom, veličinom datoteke i prepoznatom vrstom kako bi se neobični obrasci kasnije mogli pratiti.

Kod CMS-ova i shopova ugrađene medijske biblioteke obično donose vlastite provjere. Rizičnija su proširenja obrazaca, funkcije uvoza i samostalno programirane dodatne stranice koje datoteke pohranjuju mimo biblioteke. Upravo ta mjesta trebate pogledati prva pri provjeri.

Savjet

Datoteke iz zaštićene mape isporučujte s Content-Disposition: attachment i X-Content-Type-Options: nosniff. Tada preglednik problematične sadržaje ne prikazuje izravno, nego ih nudi kao preuzimanje.

Isključivanje izvršavanja PHP-a u mapi za upload

Čak i ako datoteka s pogrešnim sadržajem prođe, web-poslužitelj je ne smije izvršiti kao skriptu. Ovaj drugi zaštitni sloj pripada na svaki poslužitelj koji uploade pohranjuje u webroot.

Za Nginx s PHP-FPM-om:

location ^~ /uploads/ {
    location ~* \.(php|phtml|phar)$ {
        deny all;
    }
}

Za Apache, najbolje izravno u VirtualHostu umjesto u .htaccess, kako pravilo aplikacija ne bi mogla prepisati:

<Directory "/var/www/html/uploads">
    AllowOverride None
    <FilesMatch "\.(php|phtml|phar)$">
        Require all denied
    </FilesMatch>
</Directory>

Ako PHP radi kao Apache modul (mod_php), u istom bloku možete dodatno postaviti php_admin_flag engine off. Nakon promjene bezazlenom testnom datotekom koja samo ispisuje tekst provjerite da poslužitelj na zahtjev prema njoj odgovara s 403.

Ograničenja veličine i veza s PHP2Shellom

Uploadi su usko povezani s PHP postavkama post_max_size i upload_max_filesize. U okviru PHP2Shella php.net kao privremenu mjeru preporučuje postaviti post_max_size na 8M ako uploadi nisu potrebni, odnosno na 32M s uploadima, te upload_max_filesize držati niže. To nije potpuna zaštita i ne zamjenjuje ažuriranje na ispravljenu verziju PHP-a.

; php.ini ili FPM pool (php_admin_value), zatim ponovno pokrenuti sve workere
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Ako web-stranica uopće ne treba uploade, file_uploads = Off je najčišće rješenje. Koje su postavke smislene do zakrpe, navedeno je na stranici Hitne mjere bez zakrpe. Prikladna prava za direktorije uploada opisuje Prava datoteka na web-poslužitelju.

Tipične pogreške u funkcijama uploada

Pri pregledu postojećih aplikacija uvijek se pojavljuju isti obrasci. Ciljano provjerite svoje obrasce na ove točke:

  • Nastavak se provjerava samo djelomično, pa prolaze nazivi datoteka s više nastavaka.
  • Izvorno ime iz obrasca završava u putanji pohrane. To otvara put manipulaciji putanjom i prepisivanju postojećih datoteka.
  • Uploadi javno leže s predvidljivim imenima. Kod prijava za posao ili ugovora to je i problem zaštite podataka.
  • Mapa za upload je za web-poslužitelj istodobno upisiva i izvršiva.
  • Stara proširenja za upload ili dodaci za obrasce ne ažuriraju se iako je proizvođač objavio sigurnosna ažuriranja.
  • Pogreške se ne bilježe. Odbijeni uploadi, međutim, rani su signal ciljanih pokušaja.

Kako PHP SOS konkretno pomaže?

  • Provjeravamo Vaše funkcije uploada u kodu i konfiguraciji poslužitelja te imenujemo konkretne slabosti s prioritetom.
  • Postavljamo zabranu izvršavanja u mapi za upload, odgovarajuća prava i ograničenja veličine u okviru ojačavanja poslužitelja.
  • Kod sumnje pretražujemo direktorije uploada na ubačene skripte i čistimo ih putem našeg uklanjanja zlonamjernog softvera.
  • Na željeno provedbu neovisno provjeravamo penetracijskim testom.

Česta pitanja

Je li dovoljno provjeriti nastavak datoteke pri uploadu?

Ne. Nastavak i MIME tip koji javlja preglednik potječu od pošiljatelja i mogu se slobodno birati. Provjerite sadržaj na poslužitelju, dodijelite novo ime datoteke i zabranite izvršavanje skripti u direktoriju za upload.

Gdje bi trebalo pohraniti učitane datoteke?

Najbolje izvan javnog web-direktorija. Isporuku tada preuzima skripta koja prethodno provjerava smije li korisnik vidjeti datoteku. Tako datoteke nisu ni izravno dostupne ni izvršive.

Jesu li SVG datoteke kao slike bezazlene?

Ne automatski. SVG je XML i može sadržavati aktivne sadržaje. Dopustite SVG samo ako datoteke čistite ili ih isporučujete isključivo kao preuzimanje s odgovarajućim Content-Disposition zaglavljem.

Ima li veličina uploada veze s PHP2Shellom?

Da, neizravno. php.net kao privremenu mjeru preporučuje niže vrijednosti za post_max_size i upload_max_filesize. To smanjuje rizik, ali ne zamjenjuje ažuriranje na ispravljenu verziju PHP-a i ponovno pokretanje svih workera.

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ć