Un modulo di caricamento che accetta immagini è un modo per posare un file sul suo server. La validazione riduce ciò che passa; non rende sicura la directory. A renderla sicura è che il server web si rifiuti di ESEGUIRE qualsiasi cosa lì dentro, qualunque cosa il file si riveli essere.
Nginx
location ^~ /uploads/ {
location ~ \.php$ { deny all; }
add_header X-Content-Type-Options nosniff;
}
Apache e LiteSpeed
# /var/www/site/uploads/.htaccess
php_flag engine off
<FilesMatch "\.(php|phar|phtml|cgi|pl)$">
Require all denied
</FilesMatch>
Options -ExecCGI -Indexes
Validi anche, e come si deve
- Controlli il tipo vero, non il nome mandato da un browser:
finfo_file()in PHP. - Rinomini ogni caricamento con un nome generato da lei. Non conservi mai il nome di file del visitatore.
- Rifiuti le doppie estensioni: un file chiamato invoice.php.jpg non è una svista.
- Ricodifichi le immagini. Far passare una foto attraverso una libreria grafica toglie tutto ciò che vi era nascosto dentro.
$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';
Meglio ancora: le conservi dove il web non arriva
Tenga i caricamenti fuori dal document root e li serva tramite uno script che controlla chi sta chiedendo. Un millisecondo più lento, e allora non esiste più alcun URL capace di eseguire qualcosa.
Lo provi. Metta lei stesso un file PHP innocuo nella directory dei caricamenti e lo richieda: deve ottenere un 403 o il testo sorgente, mai l'output. Ci vuole un minuto ed è l'unica prova.