# Cum procesează PHP încărcarea fișierelor

> Cum procesează PHP intern încărcarea fișierelor: multipart/form-data, fișiere temporare în upload_tmp_dir, array-ul $_FILES, limite și coduri de eroare.

Sursa: https://phpsos.de/ro/php/datei-uploads-intern/  
Stadiu: 25.10.2026  
Furnizor: PHP SOS, Asistență de urgență PHP și securitatea serverelor. Ajutor imediat pe WhatsApp: https://wa.me/491736494914?text=Bun%C4%83%20ziua%2C%20PHP%20SOS%2C%20avem%20nevoie%20de%20ajutor%20cu%20serverul%20nostru%20PHP.%20Firma%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**Pe scurt:** Când un browser încarcă un fișier, el trimite o cerere POST în formatul `multipart/form-data`, în care câmpurile formularului și conținutul fișierelor apar ca părți individuale, una după alta. PHP descompune singur această cerere, scrie fiecare parte de tip fișier într-un fișier temporar din `upload_tmp_dir`, introduce datele în `$_FILES` și abia apoi pornește scriptul dumneavoastră. Scriptul mută fișierul cu `move_uploaded_file()`; tot ce nu este mutat, PHP șterge la sfârșitul cererii.

## Drumul unui fișier de la browser până în script

Pentru factorii de decizie, un lucru este esențial: un upload nu este o operațiune doar a aplicației. O mare parte din muncă se întâmplă în interpretorul PHP, înainte ca CMS-ul sau magazinul să fie implicat. De aceea, erorile din această parte a PHP afectează orice site, indiferent dacă are sau nu un formular de upload.

Desfășurarea, pe scurt:

1. Formularul cu `method="POST"` și `enctype="multipart/form-data"` este trimis.
2. Serverul web preia cererea și o transmite către PHP, în funcție de modul de operare prin PHP-FPM, mod_php sau altă interfață.
3. PHP verifică limite precum `post_max_size` și descompune conținutul pe baza unui șir de separare, numit boundary.
4. Câmpurile obișnuite ajung în `$_POST`, părțile de tip fișier sunt scrise în fișiere temporare și descrise în `$_FILES`.
5. Abia acum pornește scriptul, verifică fișierul și îl mută la destinație.
6. La sfârșitul cererii, PHP șterge toate fișierele temporare care nu au fost mutate.

## multipart/form-data: structura unei cereri de upload

O cerere de upload este text simplu cu date de fișier încorporate. Antetul `Content-Type` indică boundary-ul, iar în conținut acest șir separă părțile individuale. Fiecare parte are propriile linii de antet, cu numele câmpului și, după caz, numele fișierului. Un exemplu foarte scurtat și inofensiv:

```http
POST /bewerbung HTTP/1.1
Content-Type: multipart/form-data; boundary=----trenner42

------trenner42
Content-Disposition: form-data; name="vorname"

Erika
------trenner42
Content-Disposition: form-data; name="lebenslauf"; filename="lebenslauf.pdf"
Content-Type: application/pdf

%PDF-1.7 ... Dateiinhalt ...
------trenner42--
```

Toate datele din acest text provin de la expeditor, inclusiv numele fișierului și `Content-Type` al părții. PHP le preia în `$_FILES`, dar nu le verifică din punct de vedere al plauzibilității.

## Array-ul $_FILES

Pentru un câmp cu numele `lebenslauf`, `$_FILES['lebenslauf']` conține, potrivit manualului PHP, aceste chei:

| Cheie | Conținut | De încredere? |
|---|---|---|
| `name` | numele original al fișierului la expeditor | nu |
| `full_path` | calea raportată de browser, din PHP 8.1.0 | nu |
| `type` | tip MIME conform browserului, neverificat de PHP | nu |
| `size` | dimensiunea în octeți | stabilită de PHP |
| `tmp_name` | calea fișierului temporar pe server | atribuită de PHP |
| `error` | codul de eroare al procesării upload-ului | setat de PHP |

La câmpurile cu paranteze pătrate în nume, de exemplu `bilder[]`, fiecare cheie conține un array cu câte o intrare pentru fiecare fișier. Aplicația parcurge atunci, de exemplu, `$_FILES['bilder']['error']` și accesează `tmp_name` și `name` prin același index.

Un formular poate trimite în plus, înaintea câmpului de fișier, un câmp ascuns `MAX_FILE_SIZE`. Potrivit manualului, acesta este doar o indicație pentru browser, care poate fi ocolită cu ușurință. Vă puteți baza exclusiv pe limitele impuse de server: în PHP, în serverul web și în aplicația însăși.

## Limite: directivele php.ini pentru upload-uri

Aceste directive controlează procesarea upload-urilor. Valorile implicite provin din manualul PHP și se aplică atunci când niciun php.ini nu le suprascrie.

| Directivă | Implicit | Modificabilă în |
|---|---|---|
| `file_uploads` | `1` | doar configurația sistemului |
| `upload_tmp_dir` | gol (directorul temporar al sistemului) | doar configurația sistemului |
| `upload_max_filesize` | `2M` | php.ini, pool, configurația directorului |
| `post_max_size` | `8M` | php.ini, pool, configurația directorului |
| `max_file_uploads` | `20` | php.ini, pool, configurația directorului |
| `enable_post_data_reading` | `1` | php.ini, pool, configurația directorului |

`post_max_size` trebuie să fie mai mare decât `upload_max_filesize`, iar `memory_limit` ar trebui, potrivit manualului, să fie de regulă mai mare decât `post_max_size`. Dacă `upload_tmp_dir` nu poate fi scris, PHP revine la directorul temporar al sistemului. Cu `open_basedir`, directorul temporar trebuie permis, altfel upload-urile eșuează. Valorile practice și consecințele lor sunt descrise în articolul [Limitarea rezonabilă a post_max_size și upload_max_filesize](https://phpsos.de/ro/blog/post-max-size-upload-limits/).

## Evaluarea corectă a codurilor de eroare

Câmpul `error` spune ce s-a întâmplat la procesare. Constantele merg de la `UPLOAD_ERR_OK` (0), prin `UPLOAD_ERR_INI_SIZE` (1), `UPLOAD_ERR_FORM_SIZE` (2), `UPLOAD_ERR_PARTIAL` (3) și `UPLOAD_ERR_NO_FILE` (4), până la `UPLOAD_ERR_NO_TMP_DIR` (6), `UPLOAD_ERR_CANT_WRITE` (7) și `UPLOAD_ERR_EXTENSION` (8).

```php
<?php
declare(strict_types=1);

$datei = $_FILES['lebenslauf'] ?? null;
if ($datei === null) {
    exit('Keine Upload-Daten, eventuell wurde post_max_size überschritten.');
}

$meldung = match ($datei['error']) {
    UPLOAD_ERR_OK => null,
    UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'Datei zu groß.',
    UPLOAD_ERR_NO_FILE => 'Keine Datei gewählt.',
    default => 'Upload fehlgeschlagen.',
};

if ($meldung === null && is_uploaded_file($datei['tmp_name'])) {
    $ziel = '/srv/app/uploads/' . bin2hex(random_bytes(16)) . '.pdf';
    move_uploaded_file($datei['tmp_name'], $ziel);
}
```

Exemplul arată doar mecanismul. Verificarea tipului de fișier, stocarea în afara webroot-ului și blocarea execuției scripturilor sunt descrise în ghidul [Implementarea sigură a upload-urilor de fișiere în PHP](https://phpsos.de/ro/ratgeber/datei-upload-sicher/).

## Upload-uri și PHP2Shell: ce înseamnă măsurile de tranziție

Vulnerabilitățile PHP2Shell CVE-2026-109618 și CVE-2026-109619 afectează procesarea din jurul cererii în interpretorul PHP. De aceea php.net indică drept măsuri de tranziție, printre altele, `post_max_size = 8M` (fără upload-uri) sau `32M` (cu upload-uri), `upload_max_filesize` sub această valoare, precum și `session.upload_progress.enabled=0`. Aceste valori se așază în php.ini, în pool-ul FPM sau în configurația serverului, după care toți workerii trebuie reporniți.

Autori de specialitate propun suplimentar blocarea cererilor `multipart/form-data` în afara căilor reale de upload, la proxy sau în WAF. Aceasta este o măsură neoficială, cu riscul de a strica formulare; analiza compromisului este descrisă în articolul [Filtrarea cererilor de upload la margine](https://phpsos.de/ro/blog/upload-anfragen-am-rand-filtern/). Niciuna dintre aceste măsuri nu înlocuiește actualizarea la PHP 8.5.12, 8.4.27, 8.3.36 sau 8.2.35.

## Cum ajută PHP SOS concret?

- Stabilim care dintre site-urile dumneavoastră au cu adevărat nevoie de upload-uri și pe ce căi.
- Setăm limitele și măsurile de tranziție pentru fiecare pool, repornim toți workerii și testăm formularele.
- Verificăm directoarele de upload pentru fișiere executabile și blocăm execuția scripturilor în cadrul [securizării serverului](https://phpsos.de/ro/leistungen/server-haertung/).
- Instalăm actualizarea PHP și documentăm starea de patch pentru fiecare site.


## Întrebări frecvente

**Unde stochează PHP mai întâi fișierele încărcate?**  
Într-un director temporar. Implicit este directorul temporar al sistemului; cu upload_tmp_dir poate fi schimbat. Dacă fișierul nu este mutat în script, PHP îl șterge la sfârșitul cererii.

**Pot avea încredere în câmpul type din $_FILES?**  
Nu. Tipul MIME este raportat de browser, iar PHP nu îl verifică, potrivit manualului. Și name, respectiv full_path provin de la expeditor. Tipul real al fișierului îl stabiliți pe server, pe baza conținutului.

**De ce este $_FILES gol, deși a fost trimis un fișier?**  
De obicei întreaga cerere a fost mai mare decât post_max_size. În acest caz PHP golește complet $_POST și $_FILES, fără cod de eroare în array. Alte cauze sunt lipsa enctype din formular sau file_uploads dezactivat.

**Ajută împotriva PHP2Shell dacă interzic upload-urile în aplicație?**  
Nu. Procesarea datelor cererii are loc în PHP, înainte ca să ruleze codul aplicației. Eficiente sunt actualizarea PHP și repornirea tuturor workerilor; limitele mai stricte sunt doar o măsură de tranziție.

**Care este diferența dintre upload_max_filesize și post_max_size?**  
upload_max_filesize limitează fiecare fișier în parte, iar post_max_size întregul conținut al cererii, inclusiv toate fișierele și câmpurile. De aceea post_max_size trebuie să fie mai mare decât upload_max_filesize.

## Surse

- [Manualul PHP: încărcări prin POST](https://www.php.net/manual/de/features.file-upload.post-method.php)
- [Manualul PHP: mesaje de eroare la încărcări](https://www.php.net/manual/de/features.file-upload.errors.php)
- [Manualul PHP: directivele php.ini](https://www.php.net/manual/de/ini.core.php)
- [php.net: anunțuri de securitate](https://www.php.net/)
