Situația actuală: PHP2Shell. Două vulnerabilități critice PHP (CVSS 9.2) permit cod malițios fără autentificare. Patchuri din 21.10.2026.Ce este de făcut acum →

Cum implementați în siguranță încărcarea de fișiere în PHP: securitatea upload-urilor PHP

Formularele de încărcare sunt o cale frecventă pentru webshell-uri. Iată cum verificați corect tipurile de fișiere, cum salvați fișierele în siguranță și cum împiedicați serverul să execute scripturi încărcate.

Stadiu 6 min de cititGhiduri

Pe scurt: La securitatea upload-urilor PHP nu decide o singură verificare, ci combinația: verificarea tipului de fișier pe server, redenumirea fișierelor, păstrarea lor în afara webroot și nicio execuție de scripturi în dosarul de încărcări. Un formular de încărcare neverificat este una dintre căile clasice prin care atacatorii aduc o ușă din spate pe un server web.

De ce formularele de încărcare sunt o poartă de intrare preferată

Fiecare formular care primește fișiere scrie date străine pe serverul dumneavoastră. Printre ele se numără formularele de candidatură, portalurile pentru clienți, galeriile de imagini, zonele de suport și multe extensii ale CMS-urilor și ale magazinelor. Dacă un atacator reușește să depună acolo un fișier pe care serverul web îl execută apoi ca PHP, el are o ușă din spate permanentă.

Pentru conducere, aceasta înseamnă: o încărcare aparent inofensivă pentru CV-uri poate duce, în cel mai rău caz, la preluarea întregului server web. În plus, în dosarele de încărcări se află adesea date cu caracter personal, care, la o configurare greșită, pot fi accesate public.

Pentru admini și dezvoltatori, aceasta înseamnă: codul aplicației și configurarea serverului trebuie să se potrivească. O slăbiciune dintr-un nivel este adesea prinsă de celălalt, dacă ambele sunt realizate curat. Cum găsiți fișierele deja introduse descrie ghidul Cum recunoașteți și eliminați un webshell.

Regulile de bază pentru securitatea upload-urilor PHP

Regulile următoare corespund recomandărilor OWASP Cheat Sheets și s-au dovedit în exploatare:

  • Listă de permisiuni în loc de listă de blocare: Acceptați doar tipuri de fișiere permise explicit, de exemplu PDF, JPG și PNG. Listele de blocare pentru extensii individuale pot fi ocolite prin variante și extensii duble.
  • Verificați conținutul, nu doar extensia: Stabiliți tipul real pe server cu finfo. Tipul MIME din $_FILES este o indicație a expeditorului și nu este de încredere.
  • Nume nou de fișier: Nu folosiți niciodată numele original ca nume de fișier. Generați un nume aleatoriu și adăugați extensia din lista de permisiuni.
  • Păstrare în afara webroot: Salvați fișierele astfel încât să nu poată fi accesate direct prin URL. Livrarea se face printr-un script cu verificarea autorizării.
  • Limitați dimensiunea: În aplicație, în PHP și în serverul web.
  • Fără execuție: Dacă încărcările trebuie totuși să se afle în webroot, dezactivați execuția scripturilor în acel director.
  • Verificare doar pe server: Verificările JavaScript din browser îmbunătățesc utilizarea, dar nu protejează.

Un exemplu sigur cu finfo și nume aleatoriu

Exemplul următor arată nucleul unei procesări sigure. Este intenționat concis și trebuie integrat în aplicația dumneavoastră, deci cu autentificare, verificarea autorizării, protecție CSRF și înregistrare în loguri.

<?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'; // în afara 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);
// Salvați numele original doar ca text în baza de date
// și escapați-l cu htmlspecialchars() la fiecare afișare.

La imagini merită un pas suplimentar: recodificați imaginea cu GD sau Imagick, eliminând metadatele. Aceasta nu înlocuiește celelalte verificări, dar reduce riscul rezidual cauzat de date străine încorporate.

Pentru documente din surse necunoscute, de exemplu candidaturi sau fotografii pentru reclamații, o verificare antivirus înainte de transmitere este suplimentar utilă. Ea protejează mai puțin serverul web și mai mult angajații care deschid ulterior fișierele pe calculatoarele lor de lucru. Înregistrați, de asemenea, fiecare încărcare, cu ora, contul, dimensiunea fișierului și tipul recunoscut, pentru ca tiparele neobișnuite să poată fi urmărite ulterior.

La CMS-uri și magazine, bibliotecile media încorporate aduc de obicei verificări proprii. Mai riscante sunt extensiile de formulare, funcțiile de import și paginile suplimentare programate de la zero, care salvează fișiere pe lângă bibliotecă. Tocmai aceste locuri ar trebui să le priviți primele la o verificare.

Sfat

Livrați fișierele din dosarul protejat cu Content-Disposition: attachment și X-Content-Type-Options: nosniff. Atunci browserul nu afișează direct conținutul problematic, ci îl oferă ca descărcare.

Dezactivarea execuției PHP în dosarul de încărcări

Chiar dacă un fișier cu conținut greșit trece, serverul web nu trebuie să-l execute ca script. Acest al doilea strat de protecție aparține fiecărui server care salvează încărcări în webroot.

Pentru Nginx cu PHP-FPM:

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

Pentru Apache, cel mai bine direct în VirtualHost, nu într-un .htaccess, pentru ca regula să nu poată fi suprascrisă de aplicație:

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

Dacă PHP rulează ca modul Apache (mod_php), puteți seta suplimentar php_admin_flag engine off în același bloc. După modificare, verificați cu un fișier de test inofensiv, care afișează doar un text, că serverul răspunde la o cerere către el cu 403.

Limitele de dimensiune și legătura cu PHP2Shell

Încărcările sunt strâns legate de setările PHP post_max_size și upload_max_filesize. În contextul PHP2Shell, php.net recomandă ca măsură provizorie setarea post_max_size la 8M când nu sunt necesare încărcări, sau la 32M cu încărcări, și menținerea upload_max_filesize sub această valoare. Aceasta nu este o protecție completă și nu înlocuiește actualizarea la o versiune PHP corectată.

; php.ini sau pool FPM (php_admin_value), apoi reporniți toți workerii
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Dacă un site nu are nevoie deloc de încărcări, file_uploads = Off este soluția cea mai curată. Ce setări au sens până la patch găsiți la Măsuri imediate fără patch. Drepturile potrivite pentru directoarele de încărcări le descrie Drepturile fișierelor pe serverul web.

Greșeli tipice în funcțiile de încărcare

La analiza aplicațiilor existente apar mereu aceleași tipare. Verificați-vă formularele țintit pentru aceste puncte:

  • Extensia este verificată doar parțial, astfel încât numele de fișiere cu mai multe extensii trec.
  • Numele original din formular ajunge în calea de salvare. Aceasta deschide căi pentru manipularea căilor și pentru suprascrierea fișierelor existente.
  • Încărcările se află public, cu nume previzibile. La candidaturi sau contracte, aceasta este și o problemă de protecție a datelor.
  • Dosarul de încărcări poate fi scris de serverul web și este în același timp executabil.
  • Extensiile vechi de încărcare sau pluginurile de formulare nu sunt actualizate, deși producătorul a publicat actualizări de securitate.
  • Erorile nu sunt înregistrate. Încărcările respinse sunt însă un semnal timpuriu al unor încercări țintite.

Cum ajută PHP SOS concret?

  • Verificăm funcțiile dumneavoastră de încărcare în cod și în configurarea serverului și denumim slăbiciunile concrete, cu prioritate.
  • Aplicăm blocarea execuției în dosarul de încărcări, drepturi potrivite și limite de dimensiune, în cadrul securizării serverului.
  • La suspiciune, căutăm în directoarele de încărcări scripturi introduse și le curățăm prin eliminarea malware-ului.
  • La cerere, lăsăm implementarea să fie contraverificată independent printr-un test de penetrare.

Întrebări frecvente

Este suficient să verific extensia fișierului la încărcare?

Nu. Extensia și tipul MIME raportat de browser provin de la expeditor și pot fi alese liber. Verificați conținutul pe server, atribuiți un nume nou de fișier și blocați execuția scripturilor în directorul de încărcări.

Unde ar trebui salvate fișierele încărcate?

Cel mai bine în afara directorului web public. Livrarea o preia atunci un script care verifică în prealabil dacă utilizatorul are voie să vadă fișierul. Astfel, fișierele nu pot fi nici accesate direct, nici executate.

Sunt fișierele SVG inofensive ca imagini?

Nu automat. SVG este XML și poate conține conținut activ. Permiteți SVG doar dacă curățați fișierele sau le livrați exclusiv ca descărcare, cu antetul Content-Disposition potrivit.

Are dimensiunea încărcării legătură cu PHP2Shell?

Da, indirect. Ca măsură provizorie, php.net recomandă valori mai mici pentru post_max_size și upload_max_filesize. Aceasta reduce riscul, dar nu înlocuiește actualizarea la o versiune PHP corectată și repornirea tuturor workerilor.

Surse

Contact imediat

Fiecare oră contează. Scrieți-ne acum.

Descrieți pe scurt sistemul, versiunea PHP și ce ați observat. Ne vom face auziți, clarificăm situația și lucrăm la distanță până când incidentul este încheiat și documentat.

Ajutor imediat