PHP-Einfach.de
  • PHP Tutorial
  • MySQL Tutorial
  • Für Fortgeschrittene
  • Webhosting
  • Forum

Sicherer Dateiupload

17. Februar 2020
  1. Home
  2. »
  3. Für Fortgeschrittene
  4. »
  5. PHP Sicherheit
  6. »
  7. Code Injection
  8. »
  9. Sicherer Dateiupload

Viele Websites erlauben es Nutzern Dateien hochzuladen, beispielsweise Bilddateien. Dies ist allerdings mit einigen Sicherheitsrisiken verbunden. Durch einen unachtsamen Dateiupload können Angreifer beispielsweise PHP-Scripts hochladen und so beliebigen PHP-Code auf eurer Website ausführen. Der nachfolgenden Artikel zeigt die größten Sicherheitsrisiken zum Thema Dateiupload und präsentiert eine Variante für den sicheren Dateiupload.

 

Inhaltsverzeichnis

  • 1 Gefahrenquellen
  • 2 Der sichere Dateiupload
    • 2.1 Absicherung des Verzeichnis
    • 2.2 Überprüfung der Dateiendung
    • 2.3 Überprüfung ob es ein Bild ist
    • 2.4 Überprüfung der Dateigröße
    • 2.5 Vergabe eines neuen Namens
  • 3 Beispiel: Sicherer Bildupload

Gefahrenquellen

Benutzer den Upload von Dateien zu erlauben birgt mehrere Gefahrenquellen. Zum einen könnten so PHP-Script hochgeladen werden und schadhafter Code ausgeführt werden (siehe Code Injection). Eine weitere Gefahr ist, dass eine .htaccess oder eine php.ini Datei hochgeladen wird und so die Server- bzw. die PHP-Konfiguration geändert wird.

Oftmals will man den Upload von Bildern erlauben, damit Benutzer beispielsweise ein Profilbild hochladen können. Sich hier auf die $_FILES['datei']['type'] zu verlassen ist nicht ausreichend, denn diese kann beliebig vom Benutzer verändert werden. Hier die Dateiendung zu Überprüfen ist empfehlenswert.

Ein weiteres Problem beim Upload von Bildern besteht bzgl. Cross-Site-Scripting (XSS). Lädt ein Besucher eine HTML-Datei mit der Dateiendung .jpg hoch, so werden manche Browser unter Umständen dies als HTML-Datei ausführen und somit ließe sich schadhafter HTML-Code einschleusen.

Im nachfolgenden Artikel werden wir versuchen euch die wichtigsten Elemente eines sicheren Dateiuploads zu erläutern.

Der sichere Dateiupload

Für den sicheren Dateiupload empfehlen sich die folgenden Schritte:

Absicherung des Verzeichnis

Die hochgeladenen Dateien sollten in einen speziellen Ordner hochgeladen werden und dieser Ordner sollte zusätzlich geschützt werden. Wir empfehlen euch die Dateien nach z.B. uploads/files/ hochzuladen. Im Ordner uploads/ solltet ihr eine .htaccess-Datei ablegen, die das Ausführen von Scripts verhindert. Die Lösung mittels dieser zwei Ordern schütz die .htaccess-Datei davor überschrieben zu werden.

Die sicherste Variante ist per .htaccess-Datei sämtlichen Zugriff auf den Ordner zu verbieten.

1
2
Order Deny,Allow
Deny from All

Mittels PHP-Script lassen sich dann erlaubte Dateien zum Download / zur Betrachtung darstellen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
// Aufruf mittels: download.php?file=bild.png
// Überprüft, ob bild.php im Ordner 'uploads' existiert. Zeigt dieses Bild an oder bietet es zum Download an
if(!isset($_GET['file'])) {
die("Bitte eine Datei auswählen");
}
 
$filename = basename($_GET['file']);
 
 
//Optional: Nur bestimmte Datei-Endungen erlauben
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$allowed_extensions = array('png', 'jpg', 'jpeg', 'gif', 'pdf');
 
if(!in_array($extension, $allowed_extensions)) {
   die("Ungültige Dateiendung");
}
 
 
//Überprüfen ob Datei existiert
$filepath = "uploads/".$filename;
 
if(!file_exists($filepath)) {
die("Datei existiert nicht");
}
 
//Variante 1: Datei direkt im Browser anzeigen
$content_type = mime_content_type($filepath);
header('Content-type: '.$content_type);
header('Content-Disposition: inline; filename="'.$filename.'"');
readfile($filepath);
exit();
 
 
//Variante 2: Datei zum Download anbieten
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: Binary');
header('Content-disposition: attachment; filename="'.$filename.'"');
readfile($filepath);
exit();

Sollen Dateien in dem Ordner weiterhin direkt per URL aufrufbar bleiben, so sollten PHP-Scripts und ähnliches deaktiviert werden. Die folgende .htaccess-Datei deaktiviert alle Scripts im Ordern:

1
2
3
4
5
6
7
8
9
10
11
12
13
# File: uploads/.htaccess
 
# This should prevent ALL files being executed/interpreted/handled.
SetHandler none
SetHandler default-handler
 
# Disables the execution of cgi-files
Options -ExecCGI
 
# Disables the php engine
<IfModule mod_php5.c>
php_flag engine off
</IfModule>

Überprüfung der Dateiendung

Ihr solltet überprüfen, dass die hochgeladene Datei eine erlaubte Dateiendung besitzt. Es ist sicherer nur gewisse Dateiendungen, z.B. png und jpg, zu erlauben, statt zu versuchen gefährliche Dateiendungen zu begrenzen. Verwenden könnt ihr dazu folgenden Script:

1
2
3
4
5
6
$extension = strtolower(pathinfo($_FILES['datei']['name'], PATHINFO_EXTENSION));
$allowed_extensions = array('png', 'jpg', 'jpeg', 'gif');
 
if(!in_array($extension, $allowed_extensions)) {
   die("Ungültige Dateiendung");
}

Überprüfung ob es ein Bild ist

Das Überprüfen der Dateiendung bei Bildern ist nicht ausreichend, Angreifer können durch korrupte Bilddateien unter Umständen weiterhin HTML-Code einschleusen und so ein XSS-Angriff durchführen.

Das Überprüfen der $_FILES['datei']['type']-Angabe ist ebenfalls keine Lösung, da diese beliebig vom Angreifer manipuliert werden kann. Zum Überprüfung des Bildtyps könnt ihr in PHP die Funktion exif_imagetype() nutzen. Diese benötigt allerdings, dass die exinf-Erweiterung installiert ist. Sollte ihr also eine Fehlermeldung erhalten, dass die Funktion exif_imagetype nicht existent ist, so ist bei eurem Server diese Erweiterung leider nicht installiert.

1
2
3
4
5
$allowed_types = array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
$detected_type = exif_imagetype($_FILES['datei']['tmp_name']);
if(!in_array($detected_type, $allowed_types)) {
  die("Nur der Upload von Bilddateien ist gestattet");
}

Überprüfung der Dateigröße

Um das Zuspammen eures Webserver zu vermeiden solltet ihr die Dateigröße überprüfen:

1
2
3
4
$max_size = 500*1024; //500 KB
if($_FILES['datei']['size'] > $max_size) {
  die("Bitte keine Dateien größer 500kb hochladen");
}

Vergabe eines neuen Namens

Es existieren zwei Möglichkeiten, um zu vermeiden das Dateien um Upload-Order überschrieben werden. Die erste Möglichkeit ist den Namen der Datei zu erweitern, z.B. indem man eine Zahl anhängt. Dies seht ihr im Script weiter unten, dass eine Zahl anhängt falls die Datei bereits existiert.

Die andere Möglichkeit ist es,  einen neuen, zufälligen Namen zu vergeben. Den vorherigen Namen kann man beispielsweise in der Datenbank abspeichern und sofern man die Datei per PHP-Script zum Download anbietet, diesen Namen wieder mitsenden. Das Vergeben eines zufälligen Namens hat auch den Vorteil, dass keiner Dateinamen erraten kann und so Bilder/Dateien aus dem Upload-Ordner auslesen kann, die nicht für ihn bestimmt sind. Die Vergabe eines zufälligen Namens kann wie folgt aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$temporary_name = $_FILES['datei']['tmp_name'];
$extension = pathinfo($_FILES['datei']['name'], PATHINFO_EXTENSION);
 
//Überprüfung der Datei-Endung, MIME-Header Check etc.
 
function random_string() {
if(function_exists('random_bytes')) {
$bytes = random_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('mcrypt_create_iv')) {
$bytes = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$str = bin2hex($bytes);
} else {
//Bitte euer_geheim_string durch einen zufälligen String mit >12 Zeichen austauschen
$str = md5(uniqid('euer_geheimer_string', true));
}
return $str;
}
 
 
$name = random_string(); //random new name
$new_name = 'uploads/files/'.$name.'.'.$extension;
move_uploaded_file($temporary_name, $new_name);
echo "Bild hochgeladen nach: $new_name";

 

Beispiel: Sicherer Bildupload

Falls euch dies zu viele Informationen waren hier ein Beispiel für den sicheren Dateiupload von Bildern. Sollte eine Datei mit dem Namen schon vorhanden sein, so wird an den Namen noch eine Zahl angehängt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
$upload_folder = 'uploads/files/'; //Das Upload-Verzeichnis
$filename = pathinfo($_FILES['datei']['name'], PATHINFO_FILENAME);
$extension = strtolower(pathinfo($_FILES['datei']['name'], PATHINFO_EXTENSION));
//Überprüfung der Dateiendung
$allowed_extensions = array('png', 'jpg', 'jpeg', 'gif');
if(!in_array($extension, $allowed_extensions)) {
die("Ungültige Dateiendung. Nur png, jpg, jpeg und gif-Dateien sind erlaubt");
}
//Überprüfung der Dateigröße
$max_size = 500*1024; //500 KB
if($_FILES['datei']['size'] > $max_size) {
die("Bitte keine Dateien größer 500kb hochladen");
}
//Überprüfung dass das Bild keine Fehler enthält
if(function_exists('exif_imagetype')) { //exif_imagetype erfordert die exif-Erweiterung
$allowed_types = array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
$detected_type = exif_imagetype($_FILES['datei']['tmp_name']);
if(!in_array($detected_type, $allowed_types)) {
die("Nur der Upload von Bilddateien ist gestattet");
}
}
 
//Pfad zum Upload
$new_path = $upload_folder.$filename.'.'.$extension;
//Neuer Dateiname falls die Datei bereits existiert
if(file_exists($new_path)) { //Falls Datei existiert, hänge eine Zahl an den Dateinamen
$id = 1;
do {
$new_path = $upload_folder.$filename.'_'.$id.'.'.$extension;
$id++;
} while(file_exists($new_path));
}
//Alles okay, verschiebe Datei an neuen Pfad
move_uploaded_file($_FILES['datei']['tmp_name'], $new_path);
echo 'Bild erfolgreich hochgeladen: <a href="'.$new_path.'">'.$new_path.'</a>';
?>

Und im upload-Verzeichnis zusätzlich noch die folgende .htaccess-Datei ablegen, um das Ausführen von PHP-Scripts zu verhindern:

1
2
3
4
5
6
7
8
9
10
11
12
13
# File: uploads/.htaccess
# This should prevent ALL files being executed/interpreted/handled.
SetHandler none
SetHandler default-handler
# Disables the execution of cgi-files
Options -ExecCGI
# Disables the php engine
<IfModule mod_php5.c>
php_flag engine off
</IfModule>

Autor: Nils Reimers
Zurück: Code Injection
Weiter: Cross-Site-Request-Forgery (CSRF)

Für Fortgeschrittene

  • Objektorientierte Programmierung
  • PHP Sicherheit
    • Authentifizierung in PHP
    • Code Injection
      • Sicherer Dateiupload
    • Cross-Site-Request-Forgery (CSRF)
    • Cross-Site-Scripting (XSS) in PHP
    • Daten sicher speichern
    • Daten validieren
    • Penetrationtesting für PHP
    • SQL-Injections
  • Script-Beispiele
  • Codeschnipsel
  • Stellenmarkt
Mit freundlicher Unterstützung von:
  • Punkt191 Werbeagentur
  • CasinoAndy Finland
  • Casinoohnelizenz.info
  • Cryptocasinomaster.com
  • CasinoHEX.at
  • inkedin.com

Hoster – Geringste Ausfallzeit

  1. webgo Ø 1 Min.
  2. Linevast Ø 2 Min.
  3. netcup Ø 3 Min.
  4. All-Inkl.com Ø 6 Min.
  5. checkdomain Ø 8 Min.
  6. dogado Ø 17 Min.
  7. bplaced Ø 17 Min.
  8. Contabo Ø 25 Min.
  9. Hetzner Ø 49 Min.
  10. ONE.com Ø 62 Min.
» Mehr erfahren

Impressum | Datenschutz | Auf PHP-Einfach.de werben

© PHP-Einfach.de 2003 - 2023

Cookie-Zustimmung verwalten
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Anbieter verwalten Lese mehr über diese Zwecke
Einstellungen ansehen
{title} {title} {title}