Aktuellt läge: PHP2Shell. Två kritiska PHP-sårbarheter (CVSS 9,2) tillåter skadlig kod utan inloggning. Patchar sedan den 21 oktober 2026.Vad som bör göras nu →

Så hanterar PHP filuppladdningar

Mellan klicket på Ladda upp och er kod ligger en hel bearbetningskedja i PHP. Sidan förklarar multipart/form-data, temporära filer, $_FILES, gränsvärden och kopplingen till PHP2Shell.

Aktualitet 5 min läsningPHP-kunskap

Kort sagt: När en webbläsare laddar upp en fil skickar den en POST-förfrågan i formatet multipart/form-data, där formulärfält och filinnehåll står som enskilda delar efter varandra. PHP delar själv upp förfrågan, skriver varje fildel till en temporär fil i upload_tmp_dir, för in uppgifterna i $_FILES och startar först därefter ert skript. Skriptet flyttar filen med move_uploaded_file(); allt som inte flyttas raderar PHP i slutet av förfrågan.

En fils väg från webbläsaren till skriptet

För beslutsfattare är en sak viktigast: En uppladdning är inte något som bara applikationen sköter. En stor del av arbetet sker i PHP-tolken innan koden i CMS eller butik alls är inblandad. Därför påverkar fel i den här delen av PHP varje webbplats, oavsett om den har ett uppladdningsformulär eller inte.

Förloppet i korthet:

  1. Formuläret med method="POST" och enctype="multipart/form-data" skickas.
  2. Webbservern tar emot förfrågan och för den vidare till PHP, beroende på driftläge via PHP-FPM, mod_php eller ett annat gränssnitt.
  3. PHP kontrollerar gränsvärden som post_max_size och delar upp innehållet med hjälp av en avgränsande teckenföljd, den så kallade boundary.
  4. Vanliga fält hamnar i $_POST, fildelar skrivs till temporära filer och beskrivs i $_FILES.
  5. Först nu startar skriptet, kontrollerar filen och flyttar den till målplatsen.
  6. I slutet av förfrågan raderar PHP alla temporära filer som inte har flyttats.

multipart/form-data: uppbyggnaden av en uppladdningsförfrågan

En uppladdningsförfrågan är ren text med inbäddade fildata. Headern Content-Type anger boundary, och i innehållet skiljer den teckenföljden de enskilda delarna åt. Varje del har egna rubrikrader med fältnamn och eventuellt filnamn. Ett kraftigt förkortat, ofarligt exempel:

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

Alla uppgifter i den här texten kommer från avsändaren, även filnamnet och delens Content-Type. PHP för över dem till $_FILES men kontrollerar inte om de är rimliga.

Arrayen $_FILES

För ett fält med namnet lebenslauf innehåller $_FILES['lebenslauf'] enligt PHP-manualen dessa nycklar:

Nyckel Innehåll Pålitlig?
name ursprungligt filnamn hos avsändaren nej
full_path sökväg som webbläsaren rapporterat, sedan PHP 8.1.0 nej
type MIME-typ enligt webbläsaren, kontrolleras inte av PHP nej
size storlek i byte fastställs av PHP
tmp_name sökväg till den temporära filen på servern tilldelas av PHP
error felkod från uppladdningsbearbetningen sätts av PHP

För fält med hakparenteser i namnet, till exempel bilder[], innehåller varje nyckel en array med en post per fil. Applikationen går då till exempel igenom $_FILES['bilder']['error'] och når tmp_name och name via samma index.

Ett formulär kan dessutom skicka ett dolt fält MAX_FILE_SIZE före filfältet. Enligt manualen är det bara ett tips till webbläsaren som lätt kringgås. Ni kan uteslutande förlita er på de gränser som servern genomdriver: i PHP, i webbservern och i själva applikationen.

Gränsvärden: php.ini-direktiven för uppladdningar

De här direktiven styr bearbetningen av uppladdningar. Standardvärdena kommer från PHP-manualen och gäller när ingen php.ini åsidosätter dem.

Direktiv Standard Ändringsbar i
file_uploads 1 bara systemkonfigurationen
upload_tmp_dir tom (system-temp) bara systemkonfigurationen
upload_max_filesize 2M php.ini, pool, katalogkonfiguration
post_max_size 8M php.ini, pool, katalogkonfiguration
max_file_uploads 20 php.ini, pool, katalogkonfiguration
enable_post_data_reading 1 php.ini, pool, katalogkonfiguration

post_max_size måste vara större än upload_max_filesize, och memory_limit bör enligt manualen i regel vara större än post_max_size. Går det inte att skriva till upload_tmp_dir väljer PHP i stället systemets temp-katalog. Med open_basedir måste temp-katalogen vara tillåten, annars misslyckas uppladdningarna. Praktiska värden och deras följder beskrivs i inlägget Begränsa post_max_size och upload_max_filesize rimligt.

Tolka felkoder rätt

Fältet error säger vad som hände under bearbetningen. Konstanterna går från UPLOAD_ERR_OK (0) via UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) och UPLOAD_ERR_NO_FILE (4) till UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) och 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);
}

Exemplet visar bara mekaniken. Kontroll av filtyp, lagring utanför webbroten och spärr av skriptkörning beskrivs i guiden Implementera filuppladdningar i PHP säkert.

Uppladdningar och PHP2Shell: vad de tillfälliga åtgärderna innebär

PHP2Shell-sårbarheterna CVE-2026-109618 och CVE-2026-109619 gäller bearbetningen kring förfrågan i PHP-tolken. Därför nämner php.net som tillfälliga åtgärder bland annat post_max_size = 8M (utan uppladdningar) eller 32M (med uppladdningar), upload_max_filesize lägre än så, samt session.upload_progress.enabled=0. De här värdena hör hemma i php.ini, FPM-poolen eller serverkonfigurationen, därefter måste alla workers startas om.

Fackskribenter föreslår dessutom att multipart/form-data-förfrågningar utanför riktiga uppladdningssökvägar blockeras i proxyn eller WAF:en. Det är en inofficiell åtgärd med risk att formulär slutar fungera; avvägningen beskrivs i inlägget Filtrera uppladdningsförfrågningar i kanten. Ingen av de här åtgärderna ersätter uppdateringen till PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35.

Hur hjälper PHP SOS konkret?

  • Vi tar reda på vilka av era webbplatser som faktiskt behöver uppladdningar och på vilka sökvägar.
  • Vi sätter gränsvärdena och de tillfälliga åtgärderna per pool, startar om alla workers och testar formulären.
  • Vi kontrollerar uppladdningskataloger efter körbara filer och spärrar skriptkörning inom ramen för serverhärdning.
  • Vi installerar PHP-uppdateringen och dokumenterar patchläget per webbplats.

Vanliga frågor

Var sparar PHP uppladdade filer först?

I en temporär katalog. Som standard är det systemets temporära katalog; med upload_tmp_dir kan den ändras. Flyttas filen inte i skriptet raderar PHP den i slutet av förfrågan.

Kan jag lita på fältet type i $_FILES?

Nej. MIME-typen rapporteras av webbläsaren, och PHP kontrollerar den enligt manualen inte. Även name och full_path kommer från avsändaren. Den faktiska filtypen fastställer ni på serversidan utifrån innehållet.

Varför är $_FILES tom fast en fil skickades?

Oftast var hela förfrågan större än post_max_size. Då tömmer PHP $_POST och $_FILES helt, utan felkod i arrayen. Andra orsaker är ett saknat enctype i formuläret eller avstängda file_uploads.

Hjälper det mot PHP2Shell att förbjuda uppladdningar i applikationen?

Nej. Bearbetningen av förfrågningsdata sker i PHP innan applikationskoden körs. Det som fungerar är PHP-uppdateringen och omstart av alla workers; snävare gränsvärden är bara en tillfällig åtgärd.

Vad är skillnaden mellan upload_max_filesize och post_max_size?

upload_max_filesize begränsar varje enskild fil, post_max_size hela förfrågans innehåll inklusive alla filer och fält. post_max_size måste därför vara större än upload_max_filesize.

Källor

Direktkontakt

Varje timme räknas. Skriv till oss nu.

Beskriv kort system, PHP-version och observation. Vi hör av oss, klarlägger läget och arbetar på distans tills incidenten är avslutad och dokumenterad.

Direkthjälp