Sichere File-Uploads: Validierung, Speicherung, Ausführung verhindern
OWASP
0x00
Security · File-Uploads · PHP · Magento 2
Sichere File-Uploads: Validierung, Speicherung, Ausführung verhindern
MIME-Spoofing erkennen, Magic Bytes prüfen, Ausführung im Upload-Verzeichnis verhindern

Upload-Formulare zählen zu den gefährlichsten Eintrittspunkten in Webanwendungen, weil Angreifer Dateien gezielt so präparieren, dass sie jede oberflächliche Prüfung bestehen. Wer sich allein auf Dateiendung und Content-Type verlässt, öffnet die Tür für Webshells und ausführbaren Code. Dieser Artikel zeigt, wie Magic-Byte-Validierung, sichere Speicherung außerhalb des Webroots, Dateinamen-Sanitization und Bild-Reencoding echten Schutz aufbauen.

16 Min. Lesezeit MIME-Spoofing · Magic Bytes · Path Traversal · Webroot PHP 8.4 · Magento 2.4.8 · OWASP

1. Warum naive Upload-Validierung scheitert

Ein Upload-Formular ist aus Sicht eines Angreifers keine Ausnahme, sondern eine der direktesten Möglichkeiten, eigenen Code auf einen Server zu bringen. Die naive Annahme vieler Entwickler lautet: Wenn die Dateiendung stimmt und der Content-Type passt, ist die Datei sicher. Genau diese Annahme wird gezielt ausgenutzt. Ein Angreifer testet systematisch alternative PHP-Endungen wie .phtml, .php5, .pht oder .phar, manipuliert den Content-Type-Header im Multipart-Request und packt ausführbaren Code in Dateien, die auf den ersten Blick wie harmlose Bilder aussehen.

Wirksame Absicherung von Datei-Uploads funktioniert nur als mehrschichtiges System, nicht als einzelne Prüfung. Jede Schicht deckt eine andere Angriffsvariante ab: Whitelisting der erlaubten Endungen verhindert offensichtliche Fälle, Magic-Byte-Prüfung erkennt gefälschte Dateitypen, Speicherung außerhalb des Webroots verhindert die Ausführung selbst bei erfolgreichem Bypass, und Reencoding zerstört eingebetteten Code in Bilddateien vollständig. Wer nur eine dieser Schichten implementiert, baut eine Sicherheitsmaßnahme, die mit dem ersten gezielten Angriff fällt. Die folgenden Abschnitte gehen jede Schicht einzeln durch.

2. MIME-Type-Spoofing: Warum Endung und Content-Type allein nicht reichen

Der Content-Type-Header eines Multipart-Uploads wird vollständig vom Client gesetzt und ist damit ein reiner Vorschlag, kein verlässliches Signal. Ein Angreifer kann mit curl oder einem manipulierten Formular jeden beliebigen Wert senden, etwa image/jpeg für eine Datei, die tatsächlich PHP-Code enthält. Serverseitige Anwendungen, die $_FILES['datei']['type'] in PHP oder ähnliche Client-Metadaten ungeprüft übernehmen, validieren in Wirklichkeit gar nichts, sondern vertrauen blind der Angabe des Angreifers.

Auch die Dateiendung ist kein verlässliches Kriterium. Doppelte Endungen wie shell.php.jpg werden von manchen Serverkonfigurationen fälschlich als PHP ausgeführt, wenn Apache über AddHandler jede Endung im Dateinamen prüft statt nur die letzte. Null-Byte-Injection in älteren PHP-Versionen erlaubte es, Prüfungen mit shell.php%00.jpg zu umgehen. Case-Variationen wie .PhP oder alternative ausführbare Endungen wie .phtml werden von unvollständigen Blacklists oft übersehen. Deshalb gilt: Endung und Content-Type sind Hinweise für die Benutzerführung, niemals eine Sicherheitsgrenze.

3. Magic-Byte-Validierung richtig umgesetzt

Magic Bytes sind die ersten Bytes einer Datei, die das tatsächliche Dateiformat unabhängig vom Dateinamen identifizieren. Ein echtes PNG beginnt immer mit der Byte-Sequenz 89 50 4E 47, ein JPEG mit FF D8 FF. PHP stellt mit der fileinfo-Extension und der Funktion finfo_file() eine zuverlässige Methode bereit, den echten MIME-Type anhand dieser Signatur zu bestimmen, statt sich auf Client-Angaben zu verlassen. Diese Prüfung muss serverseitig direkt nach dem Empfang der temporären Upload-Datei erfolgen, bevor irgendeine weitere Verarbeitung stattfindet.

Magic-Byte-Prüfung ist notwendig, aber nicht ausreichend, denn sogenannte Polyglot-Dateien sind gleichzeitig gültige Bilder und gültige Skripte. Eine GIF-Datei kann mit einem gültigen GIF-Header beginnen und dennoch PHP-Code enthalten, der ausgeführt wird, sobald die Datei mit einer .php-Endung auf einem PHP-fähigen Server landet. Deshalb muss die Magic-Byte-Prüfung immer mit einer strikten Endungs-Whitelist und einer Storage-Strategie kombiniert werden, die Ausführung grundsätzlich verhindert, unabhängig vom Dateiinhalt.


<?php

declare(strict_types=1);

namespace Mironsoft\Security\Model\Upload;

/**
 * Validates uploaded files by inspecting their real file signature (magic bytes)
 * instead of trusting the client supplied extension or Content-Type header.
 */
final class MagicByteValidator
{
    /**
     * Maps allowed MIME types to their expected binary signatures (magic bytes).
     *
     * @var array<string, string>
     */
    private const ALLOWED_SIGNATURES = [
        'image/jpeg' => "\xFF\xD8\xFF",
        'image/png'  => "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
        'image/gif'  => "\x47\x49\x46\x38",
        'application/pdf' => "\x25\x50\x44\x46",
    ];

    /**
     * Determines the real MIME type of a file using its binary signature,
     * never trusting $_FILES['type'] which is fully client controlled.
     *
     * @param string $tmpFilePath Path to the temporary uploaded file.
     * @return string Detected MIME type or 'application/octet-stream' if unknown.
     * @throws \RuntimeException If the fileinfo extension is unavailable.
     */
    public function detectRealMimeType(string $tmpFilePath): string
    {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        if ($finfo === false) {
            throw new \RuntimeException('fileinfo extension is not available');
        }

        $mimeType = finfo_file($finfo, $tmpFilePath);
        finfo_close($finfo);

        return $mimeType !== false ? $mimeType : 'application/octet-stream';
    }

    /**
     * Verifies that a file's real MIME type is on the allowed list.
     *
     * @param string $tmpFilePath Path to the temporary uploaded file.
     * @param array<int, string> $allowedMimeTypes Whitelist of accepted MIME types.
     * @return bool True if the file's real signature matches an allowed type.
     */
    public function isAllowed(string $tmpFilePath, array $allowedMimeTypes): bool
    {
        $realMimeType = $this->detectRealMimeType($tmpFilePath);

        return in_array($realMimeType, $allowedMimeTypes, true);
    }
}

4. Uploads außerhalb des Webroots und in nicht ausführbaren Verzeichnissen speichern

Die wirksamste einzelne Maßnahme gegen File-Upload-Angriffe ist nicht die Validierung, sondern die Speicherung: Wenn eine hochgeladene Datei niemals vom Webserver interpretiert werden kann, ist es irrelevant, ob ein Angreifer eine Validierung umgangen hat. Uploads sollten grundsätzlich außerhalb des Document-Roots gespeichert werden, etwa in einem Verzeichnis wie /var/app-storage/uploads/ statt in public/uploads/. Der Zugriff erfolgt dann ausschließlich über ein PHP-Skript, das Berechtigungen prüft, die Datei aus dem geschützten Verzeichnis liest und mit korrekten Headern ausliefert.

Ist eine Ablage außerhalb des Webroots aus architektonischen Gründen nicht möglich, muss das Upload-Verzeichnis explizit als nicht ausführbar konfiguriert werden. Bei Apache verhindert eine .htaccess-Datei mit deaktivierten Handlern die Interpretation von PHP-Dateien im Verzeichnis. Bei nginx erreicht man dasselbe, indem der location-Block für Upload-Pfade PHP-Verarbeitung explizit ausschließt, statt sich auf einen globalen PHP-Handler zu verlassen. Zusätzlich sollten Dateiberechtigungen auf 644 ohne Ausführungsbit gesetzt werden, sodass selbst ein direkter Aufruf über das Betriebssystem keine Skriptausführung ermöglicht.

5. Dateinamen-Sanitization gegen Path Traversal

Der vom Nutzer übermittelte Dateiname darf niemals ungeprüft für Dateisystem-Operationen verwendet werden. Ein Angreifer kann Namen wie ../../etc/cron.d/malicious oder ..\..\config\settings.php einreichen, um mit Path-Traversal-Sequenzen aus dem vorgesehenen Upload-Verzeichnis auszubrechen und beliebige Dateien im System zu überschreiben. Ebenso gefährlich sind Sonderzeichen, Null-Bytes und übermäßig lange Dateinamen, die je nach Dateisystem und Framework zu unerwartetem Verhalten führen können.

Die robuste Lösung generiert serverseitig einen neuen, zufälligen Dateinamen, etwa auf Basis einer UUID oder eines kryptografisch sicheren Zufallswerts, und verwirft den ursprünglichen Namen vollständig bis auf die validierte Dateiendung. Der ursprüngliche Dateiname wird, falls er für die Anzeige benötigt wird, separat in der Datenbank gespeichert, niemals als tatsächlicher Dateisystempfad verwendet. basename() allein reicht nicht aus, da es Path-Traversal-Sequenzen nicht zuverlässig entfernt; die Kombination aus Whitelisting erlaubter Zeichen und vollständiger Neugenerierung des Namens ist der einzig robuste Ansatz.


<?php

declare(strict_types=1);

namespace Mironsoft\Security\Model\Upload;

/**
 * Generates safe, unpredictable filenames for uploaded files and rejects
 * any path traversal attempt in the originally submitted filename.
 */
final class FilenameSanitizer
{
    /**
     * Whitelist of file extensions that may be persisted after validation.
     *
     * @var array<int, string>
     */
    private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];

    /**
     * Builds a completely new, random filename and keeps only the validated
     * extension of the original file. The original name is never reused
     * as part of the filesystem path.
     *
     * @param string $originalFilename Filename as submitted by the client.
     * @return string Safe filename, e.g. "3f2a9c1e8b7d4a10.jpg".
     * @throws \InvalidArgumentException If the extension is not on the whitelist.
     */
    public function generateSafeFilename(string $originalFilename): string
    {
        $extension = strtolower(pathinfo($originalFilename, PATHINFO_EXTENSION));

        if (!in_array($extension, self::ALLOWED_EXTENSIONS, true)) {
            throw new \InvalidArgumentException(sprintf('Extension "%s" is not allowed', $extension));
        }

        // Cryptographically secure random name, no relation to user input
        $randomName = bin2hex(random_bytes(16));

        return sprintf('%s.%s', $randomName, $extension);
    }

    /**
     * Rejects filenames containing path traversal sequences or null bytes.
     * Use this only as a defensive check before generateSafeFilename();
     * it must never be the sole protection against path traversal.
     *
     * @param string $filename Filename to inspect.
     * @return bool True if the filename contains no traversal indicators.
     */
    public function isFreeOfTraversalSequences(string $filename): bool
    {
        if (str_contains($filename, "\0")) {
            return false;
        }

        if (str_contains($filename, '..') || str_contains($filename, '/') || str_contains($filename, '\\')) {
            return false;
        }

        return true;
    }
}

6. Bild-Reencoding gegen eingebettete Skripte und Polyglot-Payloads

Selbst nach erfolgreicher Magic-Byte-Prüfung kann eine Bilddatei zusätzliche, für die Bildanzeige irrelevante Daten enthalten, die von einem anfälligen Bestandteil der Infrastruktur ausgeführt werden. EXIF-Metadaten können Skript-Fragmente enthalten, und Polyglot-Dateien sind so konstruiert, dass sie gleichzeitig als gültiges Bild und als gültiges Skript interpretierbar sind. Die zuverlässigste Gegenmaßnahme ist Reencoding: Das hochgeladene Bild wird mit einer Bildbibliothek wie GD oder Imagick vollständig neu dekodiert und in ein neues Bild geschrieben, wobei alle Bytes, die nicht zur eigentlichen Pixelinformation gehören, verworfen werden.

Reencoding entfernt dabei automatisch EXIF-Daten, eingebettete Kommentarfelder und jeglichen Code, der außerhalb der reinen Bilddaten liegt, weil die Zielbibliothek beim Schreiben ausschließlich die dekodierten Pixel neu kodiert. Für SVG-Dateien reicht Reencoding nicht aus, da SVG als XML-Format aktive Inhalte wie <script>-Tags oder externe Referenzen enthalten kann; hier ist stattdessen eine strikte XML-Sanitization mit einer Bibliothek, die aktive Inhalte gezielt entfernt, oder ein grundsätzliches Verbot von SVG-Uploads die sicherere Wahl.


<?php

declare(strict_types=1);

namespace Mironsoft\Security\Model\Upload;

/**
 * Re-encodes an uploaded image through GD to strip any embedded scripts,
 * EXIF payloads, or polyglot content that is not part of the actual pixels.
 */
final class ImageReencoder
{
    /**
     * Decodes the source image and writes a brand new JPEG file,
     * discarding every byte that is not part of the decoded pixel data.
     *
     * @param string $sourcePath Path to the validated temporary upload.
     * @param string $destinationPath Path the clean, re-encoded file is written to.
     * @param int $quality JPEG quality between 0 and 100.
     * @return bool True on success.
     * @throws \RuntimeException If the source image cannot be decoded.
     */
    public function reencodeAsJpeg(string $sourcePath, string $destinationPath, int $quality = 85): bool
    {
        $imageInfo = getimagesize($sourcePath);
        if ($imageInfo === false) {
            throw new \RuntimeException('Source file is not a decodable image');
        }

        $image = match ($imageInfo['mime']) {
            'image/jpeg' => imagecreatefromjpeg($sourcePath),
            'image/png'  => imagecreatefrompng($sourcePath),
            'image/gif'  => imagecreatefromgif($sourcePath),
            default => throw new \RuntimeException('Unsupported source image type'),
        };

        if ($image === false) {
            throw new \RuntimeException('Failed to decode source image');
        }

        // Flatten transparency onto a white background before writing JPEG
        $width = imagesx($image);
        $height = imagesy($image);
        $flattened = imagecreatetruecolor($width, $height);
        imagefill($flattened, 0, 0, imagecolorallocate($flattened, 255, 255, 255));
        imagecopy($flattened, $image, 0, 0, 0, 0, $width, $height);

        $result = imagejpeg($flattened, $destinationPath, $quality);

        imagedestroy($image);
        imagedestroy($flattened);

        return $result;
    }
}

7. Webserver-Hardening für Upload-Verzeichnisse

Neben der Anwendungslogik muss die Webserver-Konfiguration selbst absichern, dass Dateien im Upload-Verzeichnis niemals als Skript interpretiert werden, auch dann nicht, wenn alle anderen Schutzmaßnahmen versagen. Bei nginx erreicht man das über einen dedizierten location-Block, der für den Upload-Pfad jede Weiterleitung an den PHP-FPM-Handler explizit deaktiviert und stattdessen nur statische Auslieferung erlaubt. Bei Apache übernimmt eine .htaccess-Datei im Upload-Verzeichnis mit RemoveHandler und RemoveType für PHP-Endungen dieselbe Aufgabe, vorausgesetzt AllowOverride ist für dieses Verzeichnis aktiviert.

Diese Konfiguration sollte als eigenständige, unabhängige Sicherheitsschicht verstanden werden, die nicht von der Anwendungslogik abhängt und deshalb auch dann greift, wenn ein Deployment-Fehler die Validierung im Code deaktiviert. Zusätzlich empfiehlt sich, den X-Content-Type-Options: nosniff-Header für ausgelieferte Uploads zu setzen, damit Browser den deklarierten Content-Type nicht eigenständig erraten und dadurch unbeabsichtigt HTML oder JavaScript aus einer vermeintlichen Bilddatei ausführen. Regelmäßige automatisierte Tests, die versuchen, eine Testdatei mit PHP-Endung im Upload-Verzeichnis abzurufen, decken Konfigurationsfehler frühzeitig auf.


# nginx: deny PHP execution inside the upload directory
location /media/uploads/ {
    location ~ \.php$ {
        deny all;
        return 403;
    }

    # Serve only static files, no fastcgi_pass to php-fpm here
    try_files $uri =404;
}

# Apache: .htaccess placed inside the upload directory
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|pht|phar)$">
    # Disable execution regardless of AddHandler in a parent context
    SetHandler none
    RemoveHandler .php .phtml .php3 .php4 .php5 .php7 .pht .phar
    RemoveType .php .phtml .php3 .php4 .php5 .php7 .pht .phar
    Require all denied
</FilesMatch>

Options -ExecCGI
AddType text/plain .php .phtml .php3 .php4 .php5 .php7 .pht .phar

8. Magento-spezifische Absicherung von Medien-Uploads

Magento bringt mit Magento\Framework\File\Uploader bereits eine Basis-Validierung mit, die Entwickler jedoch korrekt konfigurieren müssen. Die Methode setAllowedExtensions() definiert eine Whitelist erlaubter Dateiendungen und sollte niemals leer bleiben oder um weitreichende Endungen wie .svg ohne zusätzliche Prüfung erweitert werden. Uploader::checkMimeType() ergänzt eine MIME-Type-Prüfung, ersetzt aber, wie in Abschnitt zwei beschrieben, keine Magic-Byte-Validierung. Der Media-Storage-Pfad pub/media/ liegt im Webroot und ist standardmäßig für Katalogbilder vorgesehen, weshalb nutzergenerierte Uploads aus Kundenbereichen oder individuellen Modulen bewusst in einem separaten, nicht direkt browserzugänglichen Pfad landen sollten.

Admin-Uploads und Storefront-Uploads erfordern unterschiedliche Vertrauensstufen: Ein Administrator mit ACL-Berechtigung für den Produktkatalog gilt als vertrauenswürdiger als ein anonymer Storefront-Besucher, der etwa Anhänge in einem Kontaktformular oder individuelle Produktbilder hochlädt. Für Storefront-Uploads sollte grundsätzlich eine engere Whitelist gelten, verpflichtendes Reencoding für Bilder und eine Speicherung außerhalb von pub/media/. Magento-Module, die eigene Upload-Funktionalität implementieren, sollten den Uploader konsequent mit setAllowRenameFiles(true) und setFilesDispersion(true) konfigurieren, um vorhersehbare Dateinamen und Verzeichnisstrukturen zu vermeiden.


<?php

declare(strict_types=1);

namespace Mironsoft\Security\Model\Upload;

use Magento\Framework\File\Uploader;
use Magento\Framework\File\UploaderFactory;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\DirectoryList;

/**
 * Wraps Magento's native Uploader with a strict extension whitelist,
 * randomized filenames, and dispersed directory storage.
 */
final class SecureMediaUploader
{
    /**
     * Constructor with promoted dependencies.
     *
     * @param UploaderFactory $uploaderFactory Factory for Magento\Framework\File\Uploader.
     * @param Filesystem $filesystem Filesystem abstraction to resolve the target directory.
     */
    public function __construct(
        private readonly UploaderFactory $uploaderFactory,
        private readonly Filesystem $filesystem
    ) {
    }

    /**
     * Uploads a file from a form field into a non public storage path,
     * enforcing a strict extension whitelist and randomized naming.
     *
     * @param string $formFieldName Name of the multipart form field.
     * @return array<string, mixed> Result data returned by Magento\Framework\File\Uploader::save().
     * @throws \Exception If validation or the upload itself fails.
     */
    public function upload(string $formFieldName): array
    {
        /** @var Uploader $uploader */
        $uploader = $this->uploaderFactory->create(['fileId' => $formFieldName]);

        // Strict whitelist, never leave this empty or add .svg without extra checks
        $uploader->setAllowedExtensions(['jpg', 'jpeg', 'png', 'gif']);
        $uploader->setAllowRenameFiles(true);
        $uploader->setFilesDispersion(true);
        $uploader->checkMimeType(['image/jpeg', 'image/png', 'image/gif']);

        // var/storage is outside pub/, never directly reachable via HTTP
        $targetDir = $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR)
            ->getAbsolutePath('storage/uploads');

        return $uploader->save($targetDir);
    }
}

9. Validierungsansätze im Vergleich

Die vorangegangenen Abschnitte zeigen, dass einzelne Validierungsschritte unterschiedlich wirksam sind und teils nur eine Illusion von Sicherheit erzeugen. Die folgende Übersicht stellt unsichere, weit verbreitete Ansätze den empfohlenen, robusten Alternativen gegenüber und macht deutlich, warum mehrschichtige Absicherung in der Praxis unverzichtbar ist.

Prüfschritt Unsicherer Ansatz Empfohlener Ansatz
Dateiendung Blacklist einzelner Endungen Whitelist + Magic-Byte-Prüfung
Content-Type $_FILES['type'] vertrauen finfo_file() Signaturprüfung
Speicherort Upload in public/uploads/ Speicherung außerhalb des Webroots
Dateiname Original-Dateiname übernehmen Zufälliger Dateiname, UUID-basiert
Bildverarbeitung Datei unverändert speichern Reencoding mit GD/Imagick
Webserver-Konfiguration PHP-Ausführung im Upload-Verzeichnis erlaubt PHP-Ausführung explizit deaktiviert

Mironsoft

Sicherheitsaudits, Upload-Härtung und Magento-Absicherung für produktive Shops

Datei-Uploads professionell absichern?

Wir prüfen eure Upload-Formulare auf MIME-Spoofing, fehlende Magic-Byte-Validierung und unsichere Speicherung, und setzen die notwendigen Schutzschichten in PHP- und Magento-Anwendungen gezielt um.

Upload-Audit

Analyse aller Upload-Endpunkte auf MIME-Spoofing, Path Traversal und Ausführungsrisiken

Härtung

Magic-Byte-Validierung, Storage-Isolation und Reencoding nachrüsten

Magento-Absicherung

Uploader-Konfiguration, ACLs und Storefront-Uploads sicher trennen

10. Zusammenfassung

Sichere File-Uploads entstehen nicht durch eine einzelne clevere Prüfung, sondern durch mehrere unabhängige Sicherheitsschichten, die jede für sich einen anderen Angriffsvektor abdecken. MIME-Type und Dateiendung sind reine Hinweise, keine Sicherheitsgrenze, weil beide vollständig vom Client kontrolliert werden. Magic-Byte-Validierung mit finfo_file() erkennt gefälschte Dateitypen zuverlässig, verhindert aber keine Polyglot-Dateien, die gleichzeitig gültiges Bild und gültiges Skript sind.

Die wirksamste Einzelmaßnahme bleibt die Speicherung außerhalb des Webroots oder in einem Verzeichnis, das der Webserver explizit von der PHP-Ausführung ausschließt, kombiniert mit zufällig generierten Dateinamen gegen Path Traversal und Reencoding gegen eingebetteten Code in Bildern. In Magento ergänzt Magento\Framework\File\Uploader mit korrekt konfigurierter Extension-Whitelist diese Schichten, ersetzt sie aber nicht. Wer alle Schichten kombiniert, macht einen erfolgreichen Bypass einzelner Prüfungen praktisch wirkungslos.

Sichere File-Uploads: Das Wichtigste auf einen Blick

MIME-Spoofing

Content-Type und Dateiendung werden vom Client kontrolliert und sind keine Sicherheitsgrenze. Immer serverseitig mit Magic Bytes verifizieren.

Magic Bytes

finfo_file() erkennt echte Dateitypen zuverlässig, schützt aber nicht vor Polyglot-Dateien. Immer mit Storage-Isolation kombinieren.

Speicherung & Dateinamen

Uploads außerhalb des Webroots ablegen, PHP-Ausführung im Upload-Verzeichnis deaktivieren, Dateinamen zufällig neu generieren.

Magento-Uploads

setAllowedExtensions() strikt konfigurieren, setFilesDispersion(true) nutzen, Storefront-Uploads separat von pub/media/ absichern.

11. FAQ: Sichere File-Uploads

1Was ist MIME-Type-Spoofing bei Datei-Uploads?
Gezieltes Fälschen des Content-Type-Headers oder der Dateiendung, damit eine bösartige Datei wie ein harmloses Bild aussieht. Der Server muss den echten Typ über Magic Bytes bestimmen, nicht über Client-Angaben.
2Warum reicht eine Prüfung der Dateiendung nicht aus?
Endungen sagen nichts über den tatsächlichen Inhalt aus. Doppelte Endungen, alternative PHP-Endungen wie .phtml und Case-Tricks umgehen einfache Blacklists zuverlässig.
3Wie funktioniert Magic-Byte-Validierung?
Liest die ersten Bytes einer Datei und vergleicht sie mit bekannten Signaturen. finfo_file() aus der fileinfo-Extension übernimmt das zuverlässig in PHP.
4Warum sollten Uploads außerhalb des Webroots gespeichert werden?
Außerhalb des Document-Roots kann der Webserver die Datei niemals direkt ausliefern oder ausführen, selbst wenn andere Prüfungen umgangen wurden. Zugriff nur kontrolliert über ein PHP-Skript.
5Wie schütze ich mich vor Path Traversal bei Dateinamen?
Serverseitig einen komplett neuen, zufälligen Dateinamen generieren und den ursprünglichen Namen verwerfen. So können ../-Sequenzen nie in einen echten Dateisystempfad gelangen.
6Was ist eine Polyglot-Datei?
Eine Datei, die gleichzeitig als gültiges Bild und gültiges Skript interpretierbar ist, je nach lesendem Programm. Magic-Byte-Prüfung allein erkennt sie nicht zuverlässig.
7Warum ist Bild-Reencoding sicherer als reines Kopieren der Datei?
Reencoding dekodiert das Bild vollständig und schreibt nur die reine Pixelinformation neu. Eingebetteter Code und EXIF-Metadaten werden dabei automatisch verworfen.
8Wie sichere ich das Upload-Verzeichnis auf Webserver-Ebene ab (nginx/Apache)?
nginx: dedizierter location-Block schließt PHP-FPM-Weiterleitung für den Upload-Pfad aus. Apache: .htaccess mit RemoveHandler und RemoveType für PHP-Endungen.
9Welche Magento-Klassen sind für sichere Datei-Uploads relevant?
Magento\Framework\File\Uploader mit setAllowedExtensions(), checkMimeType(), setAllowRenameFiles(true) und setFilesDispersion(true). Ersetzt keine zusätzliche Magic-Byte-Prüfung.
10Sollte ich SVG-Uploads generell erlauben?
SVG kann als XML-Format script-Tags und externe Referenzen enthalten. Reencoding hilft hier nicht; entweder grundsätzlich verbieten oder mit dedizierter XML-Sanitization bereinigen.