Kort sagt: För PHP-uppladdningssäkerhet avgör inte en enskild kontroll utan kombinationen: kontrollera filtypen på servern, byt namn på filer, lägg dem utanför webbroten och tillåt ingen skriptkörning i uppladdningsmappen. Ett ogranskat uppladdningsformulär är en av de klassiska vägarna för angripare att föra in en bakdörr på en webbserver.
Varför uppladdningsformulär är en favoritingång
Varje formulär som tar emot filer skriver främmande data till er server. Dit hör ansökningsformulär, kundportaler, bildgallerier, supportområden och många tillägg till CMS och butiker. Lyckas en angripare lägga upp en fil där som webbservern därefter kör som PHP har angriparen en bestående bakdörr.
För ledningen betyder det: En till synes harmlös uppladdning av meritförteckningar kan i värsta fall leda till att hela webbservern tas över. Dessutom ligger i uppladdningsmappar ofta personuppgifter som vid felaktig konfiguration kan hämtas offentligt.
För administratörer och utvecklare betyder det: Applikationskod och serverkonfiguration måste passa ihop. En svaghet på ena nivån fångas ofta upp av den andra, om båda är rent genomförda. Hur ni hittar redan insmugglade filer beskriver guiden Upptäcka och ta bort webshell.
Grundreglerna för PHP-uppladdningssäkerhet
Följande regler motsvarar rekommendationerna i OWASP Cheat Sheets och har visat sig fungera i drift:
- Tillåtelselista i stället för spärrlista: Ta bara emot uttryckligen tillåtna filtyper, till exempel PDF, JPG och PNG. Spärrlistor för enskilda ändelser går att kringgå via varianter och dubbla ändelser.
- Kontrollera innehållet, inte bara ändelsen: Fastställ den verkliga typen på servern med
finfo. MIME-typen i$_FILESär en uppgift från avsändaren och inte tillförlitlig. - Nytt filnamn: Använd aldrig originalnamnet som filnamn. Skapa ett slumpmässigt namn och lägg till ändelsen från tillåtelselistan.
- Lagring utanför webbroten: Spara filer så att de inte nås direkt via URL. Utlämning sker via ett skript med behörighetskontroll.
- Begränsa storleken: I applikationen, i PHP och i webbservern.
- Ingen körning: Måste uppladdningar ändå ligga i webbroten, stäng av skriptkörning i den katalogen.
- Kontroll bara på servern: JavaScript-kontroller i webbläsaren förbättrar användbarheten men skyddar inte.
Ett säkert exempel med finfo och slumpmässigt namn
Följande exempel visar kärnan i en säker hantering. Det är medvetet kortfattat och måste byggas in i er applikation, alltså med inloggning, behörighetskontroll, CSRF-skydd och loggning.
<?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'; // utanför webbroten
$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);
// Spara originalnamnet bara som text i databasen
// och escapa det med htmlspecialchars() vid varje utmatning.
För bilder lönar sig ett ytterligare steg: koda om bilden med GD eller Imagick och släng därvid metadata. Det ersätter inte de andra kontrollerna men sänker restrisken från inbäddade främmande data.
För dokument från okända källor, till exempel ansökningar eller reklamationsfoton, är dessutom en virusskanning före vidarebefordran meningsfull. Den skyddar mindre webbservern än de medarbetare som senare öppnar filerna på sina arbetsdatorer. Logga dessutom varje uppladdning med tid, konto, filstorlek och identifierad typ, så att ovanliga mönster går att följa upp senare.
Vid CMS och butiker har de inbyggda mediebiblioteken oftast egna kontroller. Riskablare är formulärtillägg, importfunktioner och egenprogrammerade extrasidor som sparar filer vid sidan om biblioteket. Just de ställena bör ni titta på först vid en granskning.
Leverera filer från den skyddade mappen med Content-Disposition: attachment och X-Content-Type-Options: nosniff. Då visar webbläsaren inte problematiskt innehåll direkt utan erbjuder det som nedladdning.
Stänga av PHP-körning i uppladdningsmappen
Även om en fil med fel innehåll slinker igenom får webbservern inte köra den som skript. Det andra skyddslagret hör hemma på varje server som lagrar uppladdningar i webbroten.
För Nginx med PHP-FPM:
location ^~ /uploads/ {
location ~* \.(php|phtml|phar)$ {
deny all;
}
}
För Apache, helst direkt i VirtualHost i stället för i en .htaccess, så att regeln inte kan skrivas över av applikationen:
<Directory "/var/www/html/uploads">
AllowOverride None
<FilesMatch "\.(php|phtml|phar)$">
Require all denied
</FilesMatch>
</Directory>
Körs PHP som Apache-modul (mod_php) kan ni i samma block dessutom sätta php_admin_flag engine off. Kontrollera efter ändringen med en harmlös testfil som bara skriver ut en text att servern besvarar en förfrågan mot den med 403.
Storleksgränser och kopplingen till PHP2Shell
Uppladdningar hänger nära samman med PHP-inställningarna post_max_size och upload_max_filesize. I samband med PHP2Shell rekommenderar php.net som tillfällig åtgärd att sätta post_max_size till 8M om inga uppladdningar behövs, eller till 32M med uppladdningar, och att hålla upload_max_filesize under det. Det är inget fullständigt skydd och ersätter inte uppdateringen till en åtgärdad PHP-version.
; php.ini eller FPM-pool (php_admin_value), starta därefter om alla workers
file_uploads = On
post_max_size = 32M
upload_max_filesize = 20M
max_file_uploads = 5
Behöver en webbplats inga uppladdningar alls är file_uploads = Off den renaste lösningen. Vilka inställningar som är meningsfulla fram till patchen står under Omedelbara åtgärder utan patch. Lämpliga rättigheter för uppladdningskataloger beskriver Filrättigheter på webbservern.
Typiska fel i uppladdningsfunktioner
Vid genomgång av befintliga applikationer dyker samma mönster upp om och om igen. Kontrollera era formulär riktat mot dessa punkter:
- Ändelsen kontrolleras bara delvis, så att filnamn med flera ändelser slinker igenom.
- Originalnamnet från formuläret hamnar i lagringssökvägen. Det öppnar vägar för sökvägsmanipulation och överskrivning av befintliga filer.
- Uppladdningar ligger offentligt med förutsägbara namn. Vid ansökningar eller avtal är det också ett dataskyddsproblem.
- Uppladdningsmappen är skrivbar för webbservern och samtidigt körbar.
- Gamla uppladdningstillägg eller formulär-plugins uppdateras inte fast tillverkaren har publicerat säkerhetsuppdateringar.
- Fel loggas inte. Avvisade uppladdningar är dock en tidig signal om riktade försök.
Hur hjälper PHP SOS konkret?
- Vi granskar era uppladdningsfunktioner i koden och i serverkonfigurationen och pekar ut konkreta svagheter med prioritet.
- Vi sätter körningsspärren i uppladdningsmappen, lämpliga rättigheter och storleksgränser inom ramen för serverhärdningen.
- Vid misstanke söker vi igenom uppladdningskataloger efter insmugglade skript och rensar dem via vår borttagning av skadlig kod.
- På begäran låter vi genomförandet kontrolleras oberoende genom ett penetrationstest.
Vanliga frågor
Räcker det att kontrollera filändelsen vid uppladdning?
Nej. Ändelsen och den MIME-typ som webbläsaren anger kommer från avsändaren och kan väljas fritt. Kontrollera innehållet på servern, ge filen ett nytt namn och spärra skriptkörning i uppladdningskatalogen.
Var bör uppladdade filer lagras?
Helst utanför den publika webbkatalogen. Utlämningen sköts då av ett skript som först kontrollerar om användaren får se filen. Då går filerna varken att anropa direkt eller att köra.
Är SVG-filer ofarliga som bilder?
Inte automatiskt. SVG är XML och kan innehålla aktivt innehåll. Tillåt SVG bara om ni rensar filerna eller levererar dem uteslutande som nedladdning med lämplig Content-Disposition-header.
Har uppladdningsstorleken med PHP2Shell att göra?
Ja, indirekt. php.net rekommenderar som tillfällig åtgärd lägre värden för post_max_size och upload_max_filesize. Det sänker risken men ersätter inte uppdateringen till en åtgärdad PHP-version och omstarten av alla workers.