Situazione attuale: PHP2Shell. Due vulnerabilità critiche di PHP (CVSS 9,2) consentono codice dannoso senza autenticazione. Patch dal 21/10/2026.Che cosa fare ora →

Realizzare in modo sicuro l'upload di file in PHP: sicurezza dell'upload

I moduli di upload sono una via frequente per le webshell. Come verificare correttamente i tipi di file, salvarli in sicurezza e impedire al server di eseguire script caricati.

Stato 6 min di letturaGuide

In breve: nella sicurezza dell'upload in PHP non decide un singolo controllo, ma la combinazione: verificare il tipo di file lato server, rinominare i file, collocarli fuori dalla webroot e non consentire l'esecuzione di script nella cartella di upload. Un modulo di upload non verificato è una delle vie classiche con cui gli aggressori portano una backdoor su un server web.

Perché i moduli di upload sono un punto d'ingresso preferito

Ogni modulo che accetta file scrive dati altrui sul Suo server. Ne fanno parte moduli di candidatura, portali clienti, gallerie di immagini, aree di assistenza e molte estensioni di CMS e shop. Se un aggressore riesce a depositare lì un file che il server web poi esegue come PHP, ottiene una backdoor permanente.

Per la direzione significa che un upload dall'aspetto innocuo per i curriculum può, nel peggiore dei casi, portare alla presa di controllo dell'intero server web. Inoltre nelle cartelle di upload ci sono spesso dati personali, che con una configurazione errata sono accessibili pubblicamente.

Per admin e sviluppatori significa che codice applicativo e configurazione del server devono combaciare. Una debolezza a un livello viene spesso compensata dall'altro, se entrambi sono realizzati correttamente. Come trovare i file già introdotti è descritto nella guida Riconoscere e rimuovere le webshell.

Le regole di base per la sicurezza dell'upload in PHP

Le regole seguenti corrispondono alle raccomandazioni dei OWASP Cheat Sheet e si sono dimostrate valide in esercizio:

  • Elenco di consentiti invece di elenco di bloccati: accettare solo tipi di file espressamente consentiti, ad esempio PDF, JPG e PNG. Gli elenchi di blocco per singole estensioni si aggirano con varianti e doppie estensioni.
  • Verificare il contenuto, non solo l'estensione: determinare il tipo effettivo lato server con finfo. Il tipo MIME in $_FILES è un'indicazione del mittente e non è affidabile.
  • Nuovo nome del file: non usare mai il nome originale come nome del file. Generare un nome casuale e aggiungere l'estensione dall'elenco dei consentiti.
  • Collocazione fuori dalla webroot: salvare i file in modo che non siano raggiungibili direttamente tramite URL. La consegna avviene tramite uno script con verifica dei permessi.
  • Limitare la dimensione: nell'applicazione, in PHP e nel server web.
  • Nessuna esecuzione: se gli upload devono comunque stare nella webroot, disattivare l'esecuzione di script in quella directory.
  • Verifica solo sul server: i controlli JavaScript nel browser migliorano l'uso, ma non proteggono.

Un esempio sicuro con finfo e nome casuale

L'esempio seguente mostra il nucleo di un'elaborazione sicura. È volutamente conciso e va inserito nella Sua applicazione, quindi con login, verifica dei permessi, protezione CSRF e registrazione.

<?php
declare(strict_types=1);

const MAX_BYTES  = 5 * 1024 * 1024; // 5 MB
const ERLAUBT    = [
    'application/pdf' => 'pdf',
    'image/jpeg'      => 'jpg',
    'image/png'       => 'png',
];
const ZIELORDNER = '/var/www/daten/uploads'; // fuori dalla webroot

$datei = $_FILES['dokument'] ?? null;

if (!is_array($datei) || $datei['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    exit('Upload fehlgeschlagen.');
}

if ($datei['size'] > MAX_BYTES || !is_uploaded_file($datei['tmp_name'])) {
    http_response_code(400);
    exit('Datei ungültig.');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$typ   = $finfo->file($datei['tmp_name']);

if (!isset(ERLAUBT[$typ])) {
    http_response_code(415);
    exit('Dateityp nicht erlaubt.');
}

$name = bin2hex(random_bytes(16)) . '.' . ERLAUBT[$typ];
$ziel = ZIELORDNER . '/' . $name;

if (!move_uploaded_file($datei['tmp_name'], $ziel)) {
    http_response_code(500);
    exit('Speichern fehlgeschlagen.');
}

chmod($ziel, 0640);
// Salvare il nome originale solo come testo nel database
// e a ogni output applicare l'escaping con htmlspecialchars().

Per le immagini vale la pena un passo ulteriore: ricodificare l'immagine con GD o Imagick scartando i metadati. Non sostituisce gli altri controlli, ma riduce il rischio residuo dovuto a dati estranei incorporati.

Per i documenti da fonti sconosciute, ad esempio candidature o foto di reclami, è sensata in aggiunta una scansione antivirus prima dell'inoltro. Protegge meno il server web che i collaboratori che aprono poi i file sui loro computer di lavoro. Registri inoltre ogni upload con ora, account, dimensione del file e tipo riconosciuto, in modo che schemi insoliti siano ricostruibili in seguito.

Con CMS e shop le librerie multimediali integrate portano di solito controlli propri. Più rischiose sono le estensioni dei moduli, le funzioni di importazione e le pagine aggiuntive programmate in proprio, che salvano file aggirando la libreria. Proprio questi punti dovrebbero essere esaminati per primi in una verifica.

Consiglio

Consegni i file dalla cartella protetta con Content-Disposition: attachment e X-Content-Type-Options: nosniff. Il browser non mostra allora direttamente i contenuti problematici, ma li offre come download.

Disattivare l'esecuzione di PHP nella cartella di upload

Anche se un file con contenuto errato riesce a passare, il server web non deve eseguirlo come script. Questo secondo livello di protezione va su ogni server che salva upload nella webroot.

Per Nginx con PHP-FPM:

location ^~ /uploads/ {
    location ~* \.(php|phtml|phar)$ {
        deny all;
    }
}

Per Apache, meglio direttamente nel VirtualHost invece che in un .htaccess, così che la regola non possa essere sovrascritta dall'applicazione:

<Directory "/var/www/html/uploads">
    AllowOverride None
    <FilesMatch "\.(php|phtml|phar)$">
        Require all denied
    </FilesMatch>
</Directory>

Se PHP gira come modulo Apache (mod_php), nello stesso blocco può impostare in aggiunta php_admin_flag engine off. Dopo la modifica verifichi, con un file di prova innocuo che emette solo un testo, che il server risponda con 403 a una richiesta su di esso.

Limiti di dimensione e rapporto con PHP2Shell

Gli upload sono strettamente legati alle impostazioni PHP post_max_size e upload_max_filesize. Nel contesto di PHP2Shell php.net raccomanda come misura provvisoria di impostare post_max_size a 8M se non servono upload, oppure a 32M con upload, e di tenere upload_max_filesize al di sotto. Non è una protezione completa e non sostituisce l'aggiornamento a una versione corretta di PHP.

; php.ini o pool FPM (php_admin_value), poi riavviare tutti i worker
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Se un sito non ha affatto bisogno di upload, file_uploads = Off è la soluzione più pulita. Quali impostazioni siano sensate fino alla patch si trova in Misure immediate senza patch. Permessi adatti per le directory di upload sono descritti in Permessi dei file sul server web.

Errori tipici nelle funzioni di upload

Nell'esame di applicazioni esistenti ricorrono sempre gli stessi schemi. Controlli i Suoi moduli in modo mirato su questi punti:

  • L'estensione viene controllata solo in parte, così che passano nomi di file con più estensioni.
  • Il nome originale del modulo finisce nel percorso di salvataggio. Questo apre la strada alla manipolazione dei percorsi e alla sovrascrittura di file esistenti.
  • Gli upload sono pubblici con nomi prevedibili. Per candidature o contratti è anche un problema di protezione dei dati.
  • La cartella di upload è scrivibile dal server web e allo stesso tempo eseguibile.
  • Vecchie estensioni di upload o plugin per moduli non vengono aggiornati, benché il produttore abbia pubblicato aggiornamenti di sicurezza.
  • Gli errori non vengono registrati. Gli upload rifiutati sono invece un segnale precoce di tentativi mirati.

Come aiuta concretamente PHP SOS?

  • Verifichiamo le Sue funzioni di upload nel codice e nella configurazione del server e indichiamo debolezze concrete con priorità.
  • Applichiamo il blocco dell'esecuzione nella cartella di upload, permessi adatti e limiti di dimensione nell'ambito dell'hardening del server.
  • In caso di sospetto cerchiamo script introdotti nelle directory di upload e li bonifichiamo tramite la nostra rimozione di malware.
  • Su richiesta facciamo controllare l'attuazione in modo indipendente con un penetration test.

Domande frequenti

Basta controllare l'estensione del file durante l'upload?

No. L'estensione e il tipo MIME comunicato dal browser provengono dal mittente e si possono scegliere liberamente. Verifichi il contenuto lato server, assegni un nuovo nome al file e blocchi l'esecuzione di script nella directory di upload.

Dove vanno salvati i file caricati?

Meglio fuori dalla directory web pubblica. La consegna la gestisce allora uno script che verifica prima se l'utente può vedere il file. Così i file non sono né richiamabili direttamente né eseguibili.

I file SVG sono innocui come immagini?

Non automaticamente. SVG è XML e può contenere contenuti attivi. Consenta SVG solo se ripulisce i file oppure li consegna esclusivamente come download con un header Content-Disposition adatto.

La dimensione dell'upload ha qualcosa a che fare con PHP2Shell?

Sì, indirettamente. Come misura provvisoria php.net raccomanda valori più bassi per post_max_size e upload_max_filesize. Ciò riduce il rischio, ma non sostituisce l'aggiornamento a una versione corretta di PHP e il riavvio di tutti i worker.

Fonti

Contatto immediato

Ogni ora conta. Ci scriva adesso.

Descriva brevemente sistema, versione di PHP e quanto osservato. Ci faremo sentire, chiariremo la situazione e lavoreremo da remoto fino alla chiusura e alla documentazione dell'incidente.

Aiuto immediato