Upload di file in sicurezza

9 dicembre 2014

Mai fidarsi dell’utente! È una buona regola che andrebbe sempre seguita nello sviluppo di un sito Web, soprattutto quando si permette agli utenti di caricare file sul server. Ci sono infatti due insidie che principalmente incombono dietro all’upload dei file e sono:

  • Le dimensioni dei file. Non è solo un problema di banda o di limiti di spazio: se non limitate, infatti, le dimensioni dei file trasmessi potrebbero essere tali da compromettere le prestazioni del sito, fino a renderlo temporaneamente indisponibile.
  • Il contenuto dei file. È evidente che non si può permettere all’utente di trasmettere qualunque tipo di file, altrimenti potrebbe essere caricato del malware che potrebbe compromettere la sicurezza del nostro sistema.

È indispensabile, quindi, porre dei vincoli all’upload dei file.

Controlli lato cliente e lato server

Lato client, il controllo può essere effettuato via JavaScript. A questo livello, il controllo viene effettuato prima che il file venga inviato al server, evitando, quindi, i lunghi tempi di attesa del controllo server-side (parliamo di file di dimensioni considerevoli), e garantendo, quindi, una buona user experience.

Tuttavia il controllo lato client può essere facilmente aggirato: potrebbe bastare, infatti, anche solo disattivare l’esecuzione degli script.

Lato server, il controllo viene effettuato solo dopo il caricamento del file. Questo secondo livello di controllo offre maggiori garanzie di sicurezza, sebbene renda comunque necessario l’upload dei file.

L’ideale, a questo punto, è duplicare il controllo, eseguendolo una prima volta sul client, in modo da limitare i tentativi di upload ai soli utenti che hanno disattivato l’esecuzione degli script (che dovrebbero essere davvero pochi), e una seconda volta sul server, in modo da garantirsi contro utenti malintenzionati.

Supponiamo, quindi, di avere un semplice form con il solo campo di tipo file:

<form action="" method="post" enctype="multipart/form-data" id="my_form">
	<input type="file" name="my_file" id="my_file" />
	<input type="submit" name="submit" value="Submit" />
</form>

Vediamo come far sì che l’utente carichi file delle dimensioni e del tipo voluto.

Il controllo via JavaScript

Tutti i maggiori browser supportano la File API di HTML5. Questa permette di accedere a tutte le informazioni associate ai file selezionati dall’utente tramite un campo di tipo file o un’operazione di Drag & Drop. Tali informazioni, come le dimensioni ed il mime-type, permettono di controllare l’input dell’utente e ridurre i fattori di rischio associati all’upload. Consideriamo, quindi, il seguente script:

document.forms[0].addEventListener('submit', function( evt ) {
    var file = document.getElementById('my_file').files[0];

    var regex = /^(image\/)(gif|(x-)?png|p?jpeg)$/i;

    if( file && file.size < 1048576 && file.type.search(regex) != -1 ) { // 1MB
        alert ('File size: ' + file.size + '; Mime-Type: ' + file.type + ' - Success!')
    } else {
        alert ('File Not Allowed!')
        evt.preventDefault();
    }
}, false);

L’API fornisce un riferimento all’oggetto FileList, dal quale si accede ai singoli file per recuperare le informazioni necessarie.

Il codice da scaricare.

La soluzione del puro JavaScript, nonostante la semplicità, presenta un inconveniente: qualora il file superi le dimensioni volute (o non sia del tipo richiesto), non è possibile azzerare il solo input di tipo file. Si tratta di una garanzia di sicurezza, in quanto, se fosse possibile azzerare l’attributo value, sarebbe anche possibile passare un valore arbitrario, e questo renderebbe accessibile il computer dell’utente via JS.

Si potrebbe resettare l’intero form, ma questa soluzione presenta degli inconvenienti sul piano dell’usabilità.

Un’alternativa semplice ed elegante prevede il ricorso ai metodi replaceWith e clone di jQuery:

jQuery(document).ready(function($) {
	$('form input[type=file]').each(function(){

		$(this).change(function(evt){
			var input = $(this);
			var file = input.prop('files')[0];
			var regex = /^(image\/)(gif|(x-)?png|p?jpeg)$/i;

			if( file && file.size < 2 * 1048576 && file.type.search(regex) != -1 ) { // 2 MB (this size is in bytes)

				alert( 'Success!' );

			}else{

				alert( 'File non ammesso - Tipo: ' + file.type + '; dimensioni: ' + file.size );

				input.replaceWith( input.val('').clone(true) );

				evt.preventDefault();
			}	
		})
	});
});

replaceWidth sostituisce l’elemento su cui è invocato con tutto ciò che viene passato come argomento: in questo caso, è una copia dello stesso elemento input, cui è stato azzerato l’attributo value. Con questo codice, provate ora a caricare un file di dimensioni superiori a 2Mb o di un tipo diverso da un’immagine.

Il codice da scaricare.

Se vuoi aggiornamenti su Upload di file in sicurezza inserisci la tua e-mail nel box qui sotto:
 
X
Se vuoi aggiornamenti su Upload di file in sicurezza

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy