Aktuel situation: PHP2Shell. To kritiske PHP-sårbarheder (CVSS 9,2) tillader skadelig kode uden login. Rettelser siden 21.10.2026.Hvad der skal gøres nu →

Sådan håndterer PHP filuploads

Mellem klikket på Upload og jeres kode ligger en hel behandlingskæde i PHP. Siden forklarer multipart/form-data, midlertidige filer, $_FILES, grænseværdier og sammenhængen med PHP2Shell.

Status 5 min. læsetidPHP-viden

Kort sagt: Når en browser uploader en fil, sender den en POST-forespørgsel i formatet multipart/form-data, hvor formularfelter og filindhold står som enkelte dele efter hinanden. PHP opdeler selv denne forespørgsel, skriver hver fildel til en midlertidig fil i upload_tmp_dir, registrerer oplysningerne i $_FILES og starter først derefter jeres script. Scriptet flytter filen med move_uploaded_file(); alt, der ikke flyttes, sletter PHP ved afslutningen af forespørgslen.

Filens vej fra browseren til scriptet

For beslutningstagere er én ting vigtig: Et upload er ikke en proces, der alene hører til applikationen. En stor del af arbejdet sker i PHP-fortolkeren, før koden i CMS eller shop overhovedet er involveret. Derfor berører fejl i denne del af PHP ethvert website, uanset om det har en uploadformular eller ej.

Forløbet i overblik:

  1. Formularen med method="POST" og enctype="multipart/form-data" sendes af sted.
  2. Webserveren modtager forespørgslen og giver den videre til PHP, alt efter driftsform via PHP-FPM, mod_php eller en anden grænseflade.
  3. PHP kontrollerer grænseværdier som post_max_size og opdeler indholdet ved hjælp af en adskillelsesstreng, den såkaldte boundary.
  4. Almindelige felter havner i $_POST, fildele skrives til midlertidige filer og beskrives i $_FILES.
  5. Først nu starter scriptet, kontrollerer filen og flytter den til dens bestemmelsessted.
  6. Ved afslutningen af forespørgslen sletter PHP alle midlertidige filer, der ikke blev flyttet.

multipart/form-data: opbygningen af en uploadforespørgsel

En uploadforespørgsel er ren tekst med indlejrede fildata. Headeren Content-Type angiver boundary, og i indholdet adskiller denne tegnfølge de enkelte dele. Hver del har egne headerlinjer med feltnavn og eventuelt filnavn. Et stærkt forkortet, harmløst eksempel:

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--

Alle oplysninger i denne tekst stammer fra afsenderen, også delens filnavn og Content-Type. PHP overtager dem i $_FILES, men kontrollerer ikke, om de er plausible.

$_FILES-arrayet

For et felt med navnet lebenslauf indeholder $_FILES['lebenslauf'] ifølge PHP-manualen disse nøgler:

Nøgle Indhold Pålidelig?
name oprindeligt filnavn hos afsenderen nej
full_path sti oplyst af browseren, siden PHP 8.1.0 nej
type MIME-type ifølge browseren, ikke kontrolleret af PHP nej
size størrelse i bytes fastlagt af PHP
tmp_name stien til den midlertidige fil på serveren tildelt af PHP
error fejlkode fra uploadbehandlingen sat af PHP

Ved felter med kantede parenteser i navnet, f.eks. bilder[], indeholder hver nøgle et array med én post pr. fil. Applikationen gennemløber så for eksempel $_FILES['bilder']['error'] og tilgår tmp_name og name via det samme indeks.

En formular kan desuden sende et skjult felt MAX_FILE_SIZE før filfeltet. Ifølge manualen er det kun et fingerpeg til browseren, som let kan omgås. I kan udelukkende forlade jer på de grænser, som serveren håndhæver: i PHP, i webserveren og i selve applikationen.

Grænseværdier: php.ini-direktiverne for uploads

Disse direktiver styrer uploadbehandlingen. Standardværdierne stammer fra PHP-manualen og gælder, hvis ingen php.ini overskriver dem.

Direktiv Standard Kan ændres i
file_uploads 1 kun systemkonfiguration
upload_tmp_dir tom (system-temp) kun systemkonfiguration
upload_max_filesize 2M php.ini, pool, mappekonfiguration
post_max_size 8M php.ini, pool, mappekonfiguration
max_file_uploads 20 php.ini, pool, mappekonfiguration
enable_post_data_reading 1 php.ini, pool, mappekonfiguration

post_max_size skal være større end upload_max_filesize, og memory_limit bør ifølge manualen som regel være større end post_max_size. Er upload_tmp_dir ikke skrivbar, falder PHP tilbage til systemets temp-mappe. Med open_basedir skal temp-mappen være tilladt, ellers mislykkes uploads. Praktiske værdier og deres følger beskriver indlægget Fornuftig begrænsning af post_max_size og upload_max_filesize.

Fejlkoder: sådan vurderer I dem rigtigt

Feltet error fortæller, hvad der skete under behandlingen. Konstanterne spænder fra UPLOAD_ERR_OK (0) over UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) og UPLOAD_ERR_NO_FILE (4) til UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) og UPLOAD_ERR_EXTENSION (8).

<?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);
}

Eksemplet viser kun mekanikken. Kontrol af filtypen, placering uden for webroot og spærring af scriptudførelse beskrives i guiden Implementér filuploads i PHP sikkert.

Uploads og PHP2Shell: Hvad de midlertidige foranstaltninger betyder

PHP2Shell-sårbarhederne CVE-2026-109618 og CVE-2026-109619 angår behandlingen omkring forespørgslen i PHP-fortolkeren. Derfor nævner php.net som midlertidige foranstaltninger blandt andet post_max_size = 8M (uden uploads) eller 32M (med uploads), upload_max_filesize derunder, samt session.upload_progress.enabled=0. Disse værdier hører hjemme i php.ini, FPM-pool eller serverkonfiguration, derefter skal alle workers genstarte.

Fagforfattere foreslår desuden at blokere multipart/form-data-forespørgsler uden for ægte uploadstier ved proxyen eller i WAF'en. Det er en uofficiel foranstaltning med risiko for, at formularer holder op med at virke; afvejningen beskriver indlægget Filtrér uploadforespørgsler i kanten. Ingen af disse foranstaltninger erstatter opdateringen til PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35.

Hvordan hjælper PHP SOS konkret?

  • Vi finder ud af, hvilke af jeres websites der reelt har brug for uploads, og på hvilke stier.
  • Vi indstiller grænseværdierne og de midlertidige foranstaltninger pr. pool, genstarter alle workers og tester formularerne.
  • Vi kontrollerer upload-mapper for eksekverbare filer og spærrer scriptudførelse som led i serverhærdningen.
  • Vi indspiller PHP-opdateringen og dokumenterer patch-status pr. website.

Ofte stillede spørgsmål

Hvor gemmer PHP uploadede filer først?

I en midlertidig mappe. Som standard er det systemets midlertidige mappe; med upload_tmp_dir kan den ændres. Flyttes filen ikke i scriptet, sletter PHP den ved afslutningen af forespørgslen.

Kan jeg stole på feltet type i $_FILES?

Nej. MIME-typen oplyses af browseren, og ifølge manualen kontrollerer PHP den ikke. Også name og full_path stammer fra afsenderen. Den faktiske filtype afgør I på serversiden ud fra indholdet.

Hvorfor er $_FILES tom, selv om en fil blev sendt?

Oftest var hele forespørgslen større end post_max_size. Så tømmer PHP $_POST og $_FILES fuldstændigt, uden en fejlkode i arrayet. Andre årsager er et manglende enctype i formularen eller slåede file_uploads fra.

Hjælper det mod PHP2Shell at forbyde uploads i applikationen?

Nej. Behandlingen af forespørgselsdata sker i PHP, før applikationskoden kører. Effektive er PHP-opdateringen og genstart af alle workers; snævrere grænseværdier er kun en midlertidig foranstaltning.

Hvad er forskellen på upload_max_filesize og post_max_size?

upload_max_filesize begrænser hver enkelt fil, post_max_size hele forespørgslens indhold inklusive alle filer og felter. post_max_size skal derfor være større end upload_max_filesize.

Kilder

Akut kontakt

Hver time tæller. Skriv til os nu.

Beskriv kort system, PHP-version og observation. Vi vender tilbage, afklarer situationen og arbejder på afstand, indtil hændelsen er afsluttet og dokumenteret.

Akut hjælp