Sicherheitsbedenken bei Datei-Uploads auf dem eigenen Server
Ja, Skepsis bei Datei-Uploads ist absolut berechtigt. Eine Dateiendung wie
.jpg, .pdf, .docx, .zip
oder ähnliche garantiert nicht, dass eine Datei ungefährlich ist. Die Endung ist
zunächst nur ein Name. Der tatsächliche Inhalt kann trotzdem manipuliert,
schädlich oder unerwartet sein.
1. Die Dateiendung allein reicht nicht
Ein Angreifer kann eine Datei harmlos benennen, zum Beispiel:
rechnung.pdf
bild.jpg
archiv.zip
Trotzdem kann der Inhalt der Datei problematisch sein. Deshalb sollte man sich bei Uploads niemals allein auf die Dateiendung verlassen. Sinnvoll ist eine Kombination aus erlaubten Dateiendungen, Prüfung des tatsächlichen Dateityps und einer sicheren Speicherung.
Auch OWASP empfiehlt bei Datei-Uploads ausdrücklich zusätzliche Prüfungen und Schutzmaßnahmen: OWASP File Upload Cheat Sheet
2. Office-Dateien können Makros enthalten
Besonders kritisch sind alte Office-Dateien und Office-Dateien mit Makros, zum Beispiel:
doc
xls
ppt
docm
xlsm
pptm
Gerade die alten Formate .doc, .xls und .ppt
würde ich in einem allgemeinen Kunden-Upload nur erlauben, wenn es wirklich notwendig ist.
Für einfache Kundenunterlagen sind sie meist nicht erforderlich.
Eine deutlich vorsichtigere Liste erlaubter Dateiendungen wäre zum Beispiel:
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf', 'txt'];
Wenn ZIP-Dateien wirklich benötigt werden, könnte man die Liste erweitern:
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'zip'];
Noch sicherer wäre für normale Kundenunterlagen:
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf'];
3. ZIP und RAR sind besonders heikel
ZIP- und RAR-Dateien können praktisch alles enthalten, zum Beispiel:
.exe
.php
.js
.bat
.cmd
Office-Dateien mit Makros
verschachtelte Archive
sehr große entpackte Datenmengen
Ein ZIP-Archiv ist nicht automatisch gefährlich, aber es ist eine Art Blackbox. Wenn ZIP oder RAR erlaubt werden, sollte man diese Dateien niemals automatisch entpacken und schon gar nicht deren Inhalt direkt auf dem Server bereitstellen.
RAR-Dateien würde ich in einem einfachen Upload-Formular eher weglassen.
4. Der Upload-Ordner darf nichts ausführen
Der wichtigste Punkt ist der Speicherort. In vielen PHP-Scripten sieht man zum Beispiel:
define('UPLOAD_DIR', __DIR__ . '/uploads/');
Wenn dieser Ordner innerhalb der Webseite liegt und über den Browser erreichbar ist, zum Beispiel:
https://www.deine-domain.de/uploads/datei.pdf
dann sollte dort niemals PHP, HTML, CGI oder anderer Scriptcode ausführbar sein. Die sicherste Lösung wäre, Uploads außerhalb des öffentlich erreichbaren Webbereichs zu speichern. Wenn das nicht möglich ist, sollte der Upload-Ordner zusätzlich abgesichert werden.
Beispiel für eine .htaccess im Ordner uploads
Options -Indexes
<FilesMatch "\.(php|php3|php4|php5|php7|php8|phtml|phar|cgi|pl|py|sh|html|htm|js)$">
Require all denied
</FilesMatch>
RemoveHandler .php .php3 .php4 .php5 .php7 .php8 .phtml .phar .cgi .pl .py .sh
RemoveType .php .php3 .php4 .php5 .php7 .php8 .phtml .phar .cgi .pl .py .sh
php_flag engine off
Nicht jeder Hoster erlaubt jede dieser Direktiven. Falls bei einem Hoster durch
php_flag engine off ein Serverfehler entsteht, sollte diese Zeile entfernt werden.
5. Dateien auf dem Server umbenennen
Es ist gut, hochgeladene Dateien nicht unter dem Originalnamen zu speichern. Sinnvoll ist zum Beispiel eine serverseitig erzeugte Datei:
$cleanName = preg_replace('/[^a-zA-Z0-9_-]/', '_', $kundeName);
$serverFilename = time() . '_' . $cleanName . '.' . $ext;
Dadurch werden Sonderzeichen und problematische Original-Dateinamen entschärft. Der ursprüngliche Dateiname sollte nicht direkt als Speichername auf dem Server verwendet werden.
Auch die PHP-Funktion move_uploaded_file() ist sinnvoll, weil sie prüft,
ob die Datei tatsächlich über einen HTTP-POST-Upload übertragen wurde:
PHP-Dokumentation zu move_uploaded_file()
6. Maximale Dateigröße begrenzen
Ohne Größenbegrenzung kann jemand den Webspace mit sehr großen Dateien füllen. Deshalb sollte eine maximale Upload-Größe festgelegt werden:
$maxFileSize = 10 * 1024 * 1024; // 10 MB
if ($file['size'] > $maxFileSize) {
$status = 'danger';
$message = 'Fehler: Die Datei ist zu groß. Maximal erlaubt sind 10 MB.';
}
7. MIME-Type zusätzlich prüfen
Die Prüfung des MIME-Types ist nicht perfekt, aber sie ist deutlich besser als nur die Prüfung der Dateiendung:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file['tmp_name']);
$allowedMimeTypes = [
'jpg' => ['image/jpeg'],
'jpeg' => ['image/jpeg'],
'png' => ['image/png'],
'pdf' => ['application/pdf'],
'txt' => ['text/plain'],
'zip' => ['application/zip', 'application/x-zip-compressed']
];
if (!isset($allowedMimeTypes[$ext]) || !in_array($mimeType, $allowedMimeTypes[$ext], true)) {
$status = 'danger';
$message = 'Fehler: Der tatsächliche Dateityp passt nicht zur Dateiendung.';
}
Diese Prüfung ersetzt keine vollständige Sicherheitslösung, ist aber ein sinnvoller zusätzlicher Schutz.
8. Upload-Ordner nicht öffentlich auflisten
Der Upload-Ordner sollte nicht als Verzeichnis im Browser auflistbar sein. Dafür kann
man in der .htaccess verwenden:
Options -Indexes
Zusätzlich kann eine leere index.html in den Upload-Ordner gelegt werden.
9. Die Upload-PIN sollte nicht zu einfach sein
Eine einfache Beispiel-PIN wie:
define('VALID_PIN', '1234');
ist für Tests in Ordnung, aber nicht für den echten Betrieb. Besser wäre eine längere, zufällige PIN oder ein Passwort:
define('VALID_PIN', 'T7k9-Upload-2026-xQ4');
10. Einschätzung für ein einfaches Kunden-Upload-Script
Für einen kleinen, privaten Kunden-Upload mit geheimer PIN kann ein solches Script als Grundidee brauchbar sein. In der ursprünglichen Form wäre es aber zu offen, wenn sehr viele Dateitypen erlaubt sind und der Upload-Ordner öffentlich innerhalb der Webseite liegt.
Die wichtigsten Verbesserungen:
- Den Upload-Ordner per
.htaccessso absichern, dass dort nichts ausgeführt werden kann. - Die erlaubten Dateitypen stark reduzieren.
- Alte Office-Dateien und RAR-Dateien möglichst nicht erlauben.
- Eine maximale Dateigröße einbauen.
- Den MIME-Type zusätzlich prüfen.
- ZIP-Dateien nur erlauben, wenn sie wirklich benötigt werden.
- Hochgeladene Dateien niemals automatisch öffnen, entpacken oder direkt ausführen lassen.
Empfohlene Dateitypen
Für eine vorsichtige, aber noch praktikable Variante:
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'zip'];
Für eine besonders sichere Variante:
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf'];