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 →

Implementér filupload i PHP sikkert: PHP-uploadsikkerhed

Uploadformularer er en hyppig vej for webshells. Sådan kontrollerer I filtyper rigtigt, gemmer filer sikkert og forhindrer, at serveren udfører uploadede scripts.

Status 5 min. læsetidVejledninger

Kort sagt: Ved PHP-uploadsikkerhed afgør ikke en enkelt kontrol, men kombinationen: kontrollér filtypen på serveren, omdøb filer, læg dem uden for webroot, og tillad ingen scriptkørsel i upload-mappen. En ukontrolleret uploadformular er en af de klassiske veje, hvorved angribere får en bagdør ind på en webserver.

Hvorfor uploadformularer er en foretrukken indgang

Enhver formular, der modtager filer, skriver fremmede data på jeres server. Dertil hører ansøgningsformularer, kundeportaler, billedgallerier, supportområder og mange udvidelser til CMS og shops. Lykkes det en angriber dér at lægge en fil, som webserveren derefter udfører som PHP, har vedkommende en varig bagdør.

For ledelsen betyder det: Et harmløst udseende upload til CV'er kan i værste fald føre til overtagelse af hele webserveren. Desuden ligger der i upload-mapper ofte personoplysninger, som ved en forkert konfiguration kan hentes offentligt.

For administratorer og udviklere betyder det: Applikationskode og serverkonfiguration skal passe sammen. En svaghed på ét niveau bliver ofte fanget af det andet, hvis begge er gennemført rent. Hvordan I finder allerede indsmuglede filer, beskriver vejledningen Opdag og fjern webshells.

Grundreglerne for PHP-uploadsikkerhed

De følgende regler svarer til anbefalingerne i OWASP Cheat Sheets og har vist sit værd i drift:

  • Tilladelsesliste i stedet for spærreliste: Modtag kun udtrykkeligt tilladte filtyper, for eksempel PDF, JPG og PNG. Spærrelister for enkelte endelser kan omgås via varianter og dobbelte endelser.
  • Kontrollér indholdet, ikke kun endelsen: Fastslå den faktiske type på serveren med finfo. MIME-typen i $_FILES er en oplysning fra afsenderen og ikke troværdig.
  • Nyt filnavn: Brug aldrig originalnavnet som filnavn. Generér et tilfældigt navn, og hæng endelsen fra tilladelseslisten på.
  • Lagring uden for webroot: Gem filer, så de ikke kan nås direkte via URL. Der udleveres via et script med rettighedskontrol.
  • Begræns størrelsen: I applikationen, i PHP og i webserveren.
  • Ingen kørsel: Skal uploads alligevel ligge i webroot, så slå scriptkørsel fra i denne mappe.
  • Kontrol kun på serveren: JavaScript-kontroller i browseren forbedrer betjeningen, men beskytter ikke.

Et sikkert eksempel med finfo og tilfældigt navn

Det følgende eksempel viser kernen i en sikker behandling. Det er bevidst kortfattet og skal indbygges i jeres applikation, altså med login, rettighedskontrol, CSRF-beskyttelse og logning.

<?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'; // uden for 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);
// Gem kun originalnavnet som tekst i databasen
// og escap det med htmlspecialchars() ved hvert output.

Ved billeder betaler et yderligere skridt sig: kod billedet om med GD eller Imagick, og fjern metadataene. Det erstatter ikke de andre kontroller, men sænker restrisikoen fra indlejrede fremmede data.

For dokumenter fra ukendte kilder, f.eks. ansøgninger eller reklamationsfotos, er desuden en virusscanning før videregivelsen fornuftig. Den beskytter mindre webserveren end de medarbejdere, der senere åbner filerne på deres arbejdscomputere. Log desuden hvert upload med tid, konto, filstørrelse og genkendt type, så usædvanlige mønstre senere kan spores.

Ved CMS og shops har de indbyggede mediebiblioteker som regel egne kontroller med. Mere risikable er formularudvidelser, importfunktioner og selvprogrammerede ekstrasider, der gemmer filer uden om biblioteket. Netop disse steder bør I se på først ved en kontrol.

Tip

Udlevér filer fra den beskyttede mappe med Content-Disposition: attachment og X-Content-Type-Options: nosniff. Så viser browseren ikke problematisk indhold direkte, men tilbyder det som download.

Slå PHP-kørsel fra i upload-mappen

Selv hvis en fil med forkert indhold slipper igennem, må webserveren ikke udføre den som script. Dette andet beskyttelseslag hører hjemme på enhver server, der gemmer uploads i webroot.

For Nginx med PHP-FPM:

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

For Apache, bedst direkte i VirtualHost i stedet for i en .htaccess, så reglen ikke kan overskrives af applikationen:

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

Kører PHP som Apache-modul (mod_php), kan I i samme blok desuden sætte php_admin_flag engine off. Kontrollér efter ændringen med en harmløs testfil, der kun udskriver en tekst, at serveren besvarer en forespørgsel til den med 403.

Størrelsesgrænser og forbindelsen til PHP2Shell

Uploads hænger tæt sammen med PHP-indstillingerne post_max_size og upload_max_filesize. I forbindelse med PHP2Shell anbefaler php.net som overgangsforanstaltning at sætte post_max_size til 8M, hvis der ikke er brug for uploads, eller til 32M med uploads, og at holde upload_max_filesize under det. Det er ikke en fuldstændig beskyttelse og erstatter ikke opdateringen til en rettet PHP-version.

; php.ini eller FPM-pool (php_admin_value), genstart derefter alle workere
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Har et website slet ikke brug for uploads, er file_uploads = Off den reneste løsning. Hvilke indstillinger der er fornuftige frem til patchen, står under Akutforanstaltninger uden patch. Passende rettigheder til upload-mapper beskriver Filrettigheder på webserveren.

Typiske fejl i upload-funktioner

Ved gennemgang af eksisterende applikationer dukker de samme mønstre op igen og igen. Kontrollér jeres formularer målrettet for disse punkter:

  • Endelsen kontrolleres kun delvist, så filnavne med flere endelser slipper igennem.
  • Originalnavnet fra formularen havner i lagringsstien. Det åbner veje for stimanipulation og overskrivning af eksisterende filer.
  • Uploads ligger offentligt med forudsigelige navne. Ved ansøgninger eller kontrakter er det også et databeskyttelsesproblem.
  • Upload-mappen kan skrives af webserveren og kan samtidig udføres.
  • Gamle upload-udvidelser eller formularplugins opdateres ikke, selv om producenten har offentliggjort sikkerhedsopdateringer.
  • Fejl logges ikke. Afviste uploads er dog et tidligt signal om målrettede forsøg.

Hvordan hjælper PHP SOS konkret?

  • Vi gennemgår jeres upload-funktioner i koden og i serverkonfigurationen og angiver konkrete svagheder med prioritet.
  • Vi sætter kørselsspærringen i upload-mappen, passende rettigheder og størrelsesgrænser inden for rammerne af serverhærdningen.
  • Ved mistanke gennemsøger vi upload-mapper for indsmuglede scripts og renser dem via vores malwarefjernelse.
  • Efter ønske lader vi gennemførelsen kontrollere uafhængigt ved en penetrationstest.

Ofte stillede spørgsmål

Er det nok at kontrollere filendelsen ved upload?

Nej. Endelsen og den af browseren meldte MIME-type stammer fra afsenderen og kan vælges frit. Kontrollér indholdet på serveren, tildel et nyt filnavn, og spær scriptkørsel i upload-mappen.

Hvor bør uploadede filer gemmes?

Bedst uden for den offentlige webmappe. Udleveringen overtager så et script, som forinden kontrollerer, om brugeren må se filen. Så kan filer hverken kaldes direkte eller udføres.

Er SVG-filer som billeder ufarlige?

Ikke automatisk. SVG er XML og kan indeholde aktivt indhold. Tillad kun SVG, hvis I renser filerne eller udelukkende udleverer dem som download med en passende Content-Disposition-header.

Har uploadstørrelsen noget med PHP2Shell at gøre?

Ja, indirekte. php.net anbefaler som overgangsforanstaltning lavere værdier for post_max_size og upload_max_filesize. Det sænker risikoen, men erstatter ikke opdateringen til en rettet PHP-version og genstarten af alle workere.

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