Ein Uploadformular, das Bilder annimmt, ist ein Weg, eine Datei auf Ihren Server zu legen. Prüfung verringert, was durchkommt; sicher macht sie das Verzeichnis nicht. Sicher wird es dadurch, dass der Webserver sich weigert, dort irgendetwas AUSZUFÜHREN, was auch immer die Datei sein mag.

Nginx

location ^~ /uploads/ {
    location ~ \.php$ { deny all; }
    add_header X-Content-Type-Options nosniff;
}

Apache und LiteSpeed

# /var/www/site/uploads/.htaccess
php_flag engine off
<FilesMatch "\.(php|phar|phtml|cgi|pl)$">
    Require all denied
</FilesMatch>
Options -ExecCGI -Indexes

Zusätzlich prüfen, und zwar richtig

  • Prüfen Sie den echten Typ, nicht den Namen, den ein Browser geschickt hat: finfo_file() in PHP.
  • Benennen Sie jeden Upload in einen Namen um, den Sie erzeugt haben. Behalten Sie nie den Dateinamen des Besuchers.
  • Weisen Sie doppelte Endungen ab - eine Datei namens invoice.php.jpg ist kein Versehen.
  • Kodieren Sie Bilder neu. Ein Bild durch eine Bildbibliothek zu schicken entfernt alles, was darin versteckt war.
$f = new finfo(FILEINFO_MIME_TYPE);
$type = $f->file($tmp);
if (!in_array($type, ['image/jpeg','image/png','image/webp'], true)) {
    // reject
}
$name = bin2hex(random_bytes(16)) . '.webp';

Besser noch: dort speichern, wo das Web nicht hinkommt

Halten Sie Uploads außerhalb des Document Root und liefern Sie sie über ein Skript aus, das prüft, wer fragt. Eine Millisekunde langsamer - und es gibt dann keine URL, die irgendetwas ausführen könnte.

Testen Sie es. Legen Sie selbst eine harmlose PHP-Datei ins Upload-Verzeichnis und rufen Sie sie auf: Sie müssen 403 oder den Quelltext bekommen, niemals die Ausgabe. Das dauert eine Minute und ist der einzige Beweis.