Aktuálna situácia: PHP2Shell. Dve kritické zraniteľnosti PHP (CVSS 9.2) umožňujú spustenie škodlivého kódu bez prihlásenia. Záplaty od 21. 10. 2026.Čo treba urobiť teraz →

Bezpečne riešiť upload súborov v PHP: bezpečnosť uploadu PHP

Formuláre na upload sú častou cestou pre webshelly. Ako správne kontrolovať typy súborov, bezpečne ich ukladať a zabrániť serveru vo vykonávaní nahraných skriptov.

Stav 5 min čítaniaPríručky

Stručne: Pri bezpečnosti uploadu PHP nerozhoduje jedna kontrola, ale ich kombinácia: typ súboru kontrolovať na serveri, súbory premenovať, ukladať ich mimo webroot a v priečinku s uploadmi nepovoliť vykonávanie skriptov. Nekontrolovaný formulár na upload je jednou z klasických ciest, ktorými útočníci dostanú backdoor na webový server.

Prečo sú formuláre na upload obľúbenou bránou

Každý formulár, ktorý prijíma súbory, zapisuje cudzie údaje na váš server. Patria sem formuláre na prihlášky, zákaznícke portály, galérie obrázkov, oblasti podpory a mnohé rozšírenia CMS a e-shopov. Ak sa útočníkovi podarí tam uložiť súbor, ktorý webový server následne vykoná ako PHP, má trvalý backdoor.

Pre vedenie to znamená: nevinne pôsobiaci upload životopisov môže v najhoršom prípade viesť k prevzatiu celého webového servera. Okrem toho v priečinkoch s uploadmi často ležia osobné údaje, ktoré sú pri nesprávnej konfigurácii verejne dostupné.

Pre adminov a vývojárov to znamená: kód aplikácie a konfigurácia servera musia spolu ladiť. Slabinu na jednej úrovni často zachytí druhá, ak sú obe čisto implementované. Ako nájdete už zavlečené súbory, popisuje príručka Rozpoznať webshell a odstrániť ju.

Základné pravidlá bezpečnosti uploadu PHP

Nasledujúce pravidlá zodpovedajú odporúčaniam OWASP Cheat Sheets a v prevádzke sa osvedčili:

  • Zoznam povolených namiesto zoznamu zakázaných: Prijímať iba výslovne povolené typy súborov, napríklad PDF, JPG a PNG. Zoznamy zakázaných jednotlivých prípon sa dajú obísť variantmi a dvojitými príponami.
  • Kontrolovať obsah, nielen príponu: Skutočný typ zisťovať na serveri pomocou finfo. Typ MIME v $_FILES je údaj odosielateľa a nie je dôveryhodný.
  • Nový názov súboru: Pôvodný názov nikdy nepoužívať ako názov súboru. Vytvoriť náhodný názov a pripojiť príponu zo zoznamu povolených.
  • Uloženie mimo webroot: Súbory ukladať tak, aby neboli priamo dostupné cez URL. Doručuje sa cez skript s kontrolou oprávnení.
  • Obmedziť veľkosť: V aplikácii, v PHP aj vo webovom serveri.
  • Žiadne vykonávanie: Ak uploady predsa musia ležať vo webroot, vypnúť v tomto adresári vykonávanie skriptov.
  • Kontrola iba na serveri: Kontroly JavaScriptom v prehliadači zlepšujú obsluhu, ale nechránia.

Bezpečná ukážka s finfo a náhodným názvom

Nasledujúca ukážka zobrazuje jadro bezpečného spracovania. Je zámerne stručná a musí sa vložiť do vašej aplikácie, teda s prihlásením, kontrolou oprávnení, ochranou CSRF a zaznamenávaním.

<?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'; // mimo webroot

$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);
// Pôvodný názov ukladať do databázy iba ako text
// a pri každom výstupe ho escapovať pomocou htmlspecialchars().

Pri obrázkoch sa oplatí ďalší krok: obrázok znova zakódovať pomocou GD alebo Imagick a pritom zahodiť metadáta. Nenahrádza to ostatné kontroly, ale znižuje zvyškové riziko spôsobené vloženými cudzími údajmi.

Pre dokumenty z neznámych zdrojov, napríklad prihlášky alebo fotografie reklamácií, má zmysel aj antivírusová kontrola pred ďalším odovzdaním. Chráni menej webový server než zamestnancov, ktorí súbory neskôr otvárajú na svojich pracovných počítačoch. Zaznamenávajte navyše každý upload s časom, účtom, veľkosťou súboru a rozpoznaným typom, aby sa neobvyklé vzory dali neskôr dohľadať.

Pri CMS a e-shopoch zabudované mediálne knižnice zvyčajne prinášajú vlastné kontroly. Riskantnejšie sú rozšírenia formulárov, funkcie importu a vlastnoručne naprogramované doplnkové stránky, ktoré ukladajú súbory popri knižnici. Práve tieto miesta by ste mali pri kontrole preveriť ako prvé.

Tip

Súbory z chráneného priečinka doručujte s Content-Disposition: attachment a X-Content-Type-Options: nosniff. Prehliadač potom problematický obsah nezobrazí priamo, ale ponúkne ho na stiahnutie.

Vypnúť vykonávanie PHP v priečinku s uploadmi

Aj keď súbor s nesprávnym obsahom prekĺzne, webový server ho nesmie vykonať ako skript. Táto druhá ochranná vrstva patrí na každý server, ktorý ukladá uploady do webroot.

Pre Nginx s PHP-FPM:

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

Pre Apache, najlepšie priamo vo VirtualHost namiesto .htaccess, aby pravidlo nemohla aplikácia prepísať:

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

Ak PHP beží ako modul Apache (mod_php), môžete v tom istom bloku navyše nastaviť php_admin_flag engine off. Po zmene overte neškodným testovacím súborom, ktorý vypisuje iba text, že server na požiadavku naň odpovie kódom 403.

Limity veľkosti a súvislosť s PHP2Shell

Uploady úzko súvisia s nastaveniami PHP post_max_size a upload_max_filesize. V rámci PHP2Shell php.net odporúča ako prechodné opatrenie nastaviť post_max_size na 8M, ak nie sú uploady potrebné, alebo na 32M s uploadmi, a upload_max_filesize držať pod ním. Nie je to úplná ochrana a nenahrádza aktualizáciu na opravenú verziu PHP.

; php.ini alebo pool FPM (php_admin_value), potom reštartovať všetkých workerov
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Ak web uploady vôbec nepotrebuje, najčistejším riešením je file_uploads = Off. Ktoré nastavenia dávajú zmysel do záplaty, je na stránke Okamžité opatrenia bez záplaty. Vhodné práva pre adresáre s uploadmi popisuje Práva k súborom na webovom serveri.

Typické chyby vo funkciách uploadu

Pri prehliadke existujúcich aplikácií sa stále objavujú tie isté vzory. Svoje formuláre cielene skontrolujte na tieto body:

  • Prípona sa kontroluje iba čiastočne, takže prekĺznu názvy súborov s viacerými príponami.
  • Pôvodný názov z formulára skončí v ceste uloženia. To otvára cesty k manipulácii s cestami a prepisovaniu existujúcich súborov.
  • Uploady ležia verejne s predvídateľnými názvami. Pri prihláškach alebo zmluvách je to aj problém ochrany údajov.
  • Priečinok s uploadmi je pre webový server zapisovateľný a zároveň spustiteľný.
  • Staré rozšírenia uploadu alebo pluginy formulárov sa neaktualizujú, hoci výrobca zverejnil bezpečnostné aktualizácie.
  • Chyby sa nezaznamenávajú. Odmietnuté uploady sú pritom včasným signálom cielených pokusov.

Ako konkrétne pomáha PHP SOS?

  • Vaše funkcie uploadu kontrolujeme v kóde aj v konfigurácii servera a konkrétne slabiny pomenúvame s prioritou.
  • Blokovanie vykonávania v priečinku s uploadmi, vhodné práva a limity veľkosti nastavujeme v rámci spevnenia servera.
  • Pri podozrení prehľadáme adresáre s uploadmi kvôli zavlečeným skriptom a vyčistíme ich cez naše odstránenie malvéru.
  • Na želanie necháme realizáciu nezávisle preveriť penetračným testom.

Časté otázky

Stačí pri uploade skontrolovať príponu súboru?

Nie. Prípona a typ MIME hlásený prehliadačom pochádzajú od odosielateľa a dajú sa ľubovoľne zvoliť. Kontrolujte obsah na serveri, prideľte nový názov súboru a zablokujte vykonávanie skriptov v adresári s uploadmi.

Kde by sa mali nahrané súbory ukladať?

Najlepšie mimo verejného webového adresára. Doručovanie potom preberá skript, ktorý predtým skontroluje, či smie používateľ súbor vidieť. Súbory tak nie sú ani priamo dostupné, ani spustiteľné.

Sú súbory SVG ako obrázky bezpečné?

Nie automaticky. SVG je XML a môže obsahovať aktívny obsah. SVG povoľte iba vtedy, ak súbory vyčistíte alebo ich doručujete výlučne ako stiahnutie s vhodnou hlavičkou Content-Disposition.

Súvisí veľkosť uploadu s PHP2Shell?

Áno, nepriamo. php.net odporúča ako prechodné opatrenie nižšie hodnoty pre post_max_size a upload_max_filesize. To znižuje riziko, ale nenahrádza aktualizáciu na opravenú verziu PHP a reštart všetkých workerov.

Zdroje

Okamžitý kontakt

Každá hodina sa počíta. Napíšte nám hneď.

Stručne opíšte systém, verziu PHP a svoje pozorovanie. Ozveme sa, vyjasníme situáciu a budeme pracovať na diaľku, kým nebude incident uzavretý a zdokumentovaný.

Okamžitá pomoc