Come gestire i file: il download

Negli articoli precedenti (Come gestire i file: le basi parte 1 e parte 2) abbiamo visto come creare la classe YCFile. Con questo articolo aggiungeremo un ulteriore metodo alla classe oltre a studiare un esempio di un suo utilizzo pratico in uno degli ambiti più comuni in un sito web rispondendo a questa domanda: come è meglio muoversi per evitare il download diretto dei file e preservare i nostri dati da occhi indiscreti

Struttura della guida

Come gestire i file: le basi (parte 1)
Questo primo articolo è incentrato sulla creazione di una classe per gestire i file. Ti mostro come effettuare controlli di sicurezza preliminari e alcuni metodi per ottenere informazioni basilari.

Come gestire i file: le basi (parte 2)
Ecco come cercare, leggere ed eliminare i file, effettuando controlli a monte ed eliminando ogni futuro e possibile problema.

Come gestire i file: il download
Vediamo come effettuare il download dei file in sicurezza per te e per i tuoi utenti.

Come gestire i file: l’upload
Ed infine ecco come effettuare l’upload dei file sul server, con l’ausilio della classe class.upload.

 

Introduzione al download

È noto a tutti i programmatori che il web server (che sia Apache o IIS), non mostra mai a schermo il codice di una pagina PHP poichè, prima di inviarla al browser, viene eseguita e ad esso viene inviato solo il risultato di tale pagina.

Ma cosa succede se invece di un URL del tipo https://www.miosito.com/index.php  chiedo un URL del tipo https://www.miosito.com/mioFile.zip?
Il web server invia il file al browser così com’è e viene mostrata una finestra per scaricare o aprire il file.
Non potremmo fare nessun controllo sul file. Potremmo aver bisogno di rinominarlo in un determinato modo o di impedire il download in un determinato caso. Come possiamo impedire questo comportamento standard del browser?

Tu non puoi passare! Prima soluzione

Esistono due metodi per evitare l’accesso diretto ai file.
Il primo è tramite htaccess ma questo può funzionare solo su web server Apache.
Non vi spiegherò nel dettaglio il suo funzionamento poichè non è l’argomento che stiamo trattando. Vi basti sapere che inserendo il seguente codice all’interno di un file di testo e rinominandolo in .htaccess, impediremo l’accesso diretto ai file posizionati nella cartella in cui esso si trova e in tutte le sue sottocartelle.

<Files ~ “.+”>
Order allow,deny
Deny from all
Satisfy All
</Files>

Tu non puoi passare! Seconda soluzione

La seconda soluzione, prevede l’utilizzo di una pagina PHP dalla quale far passare la richiesta di download ed è quella che utilizzeremo noi. Inizia con il creare la tua pagina force-download.php.
La pagina verrà richiamata dal browser nel seguente modo: http:://www.miosito.it/force-download.php?file=xxxxxxx

Analizzando l’URL, noti che c’è una variabile GET con valore xxxxx.
Questo valore verrà successivamente sostituito dal nome del file che intendiamo far scaricare e in questo caso, sarà anche criptato tramite la funzione base64_encode().
Se ti chiedi perchè, il motivo è semplice. Non vogliamo mai far sapere agli utenti come si chiama realmente il file sul server per motivi di sicurezza (la codifica base64 è poco sicura. Se avete a disposizione un database utilizzate MD5 o meglio ancora uno SHA).

Iniziamo a scrivere il codice necessario per fare ciò, quindi apriamo il file force-download.php e inseriamo questo codice:

require_once('ycfile.php');
define('DS', DIRECTORY_SEPARATOR);

//Questa variabile contiene il percorso relativo alla cartella dove sono contenuti i file.
$basePath = 'uploads' . DS;
$fileName = $_GET['file'];

//Il nome del file viene decodificato.
//Questo passaggio è necessario nel caso non si voglia lasciare il nome del file in chiaro
//nell'URL e quindi lo si passa a questa pagina criptato con base64_encode().
$fileNameDecoded = base64_decode($fileName);

Stiamo ipotizzando un caso in cui non utilizziamo un database per cercare il file. Altrimenti avremmo potuto passare come variabile GET, l’id del record del file (magari criptato con un algoritmo della famiglia SHA).

Ora dobbiamo tornare alla classe YCFile ed aggiungere il metodo YCFile::download().

/* Ometto il codice della classe trattato negli altri articoli */

   //Prepara gli header per il download del file e lo invia al browser.
	public static function download($file) {
		if(self::isReadable($file)) {
			$fileSize = filesize($file);
			$fileRealName = self::sanitizeName(self::realName($file));

			if($fp = @fopen($file, 'rb')) {
				header("Pragma: public");
				header("Cache-Control: no-store, no-cache, must-revalidate");
				header("Content-type: application/force-download");
				header("Content-Transfer-Encoding: binary");
				header("Content-description: File transfer");
				header("Content-disposition: attachment; filename=\"". $fileRealName ."\"");
				header("Content-length: ". $fileSize);

				fpassthru($fp);
				fclose($fp);
            return true;
			}
		}

		return false;
	}

Bene, con questa funzione siamo pronti a far scaricare il file agli utenti.
Essa non fa altro che fare i soliti controlli di sicurezza sull’esistenza e sulla leggibilità del file.
Successivamente prepara e invia gli header al browser per far scaricare il file.

Analizziamo questo header:

header("Content-type: application/force-download");

Esso obbliga il browser a far scaricare il file. Sicuramente ti sarà capitato di aver letto un file PDF direttamente nel tuo browser. Questo comando impedisce esattamente questo comportamento.

Ora abbiamo veramente completato la classe YCFile. È pronta anche a far scaricare i file.
Torniamo quindi allo script force-download.php ed aggiungiamo altro codice.

Supponiamo di avere una struttura del genere:

Ipotetica impostazione di cartelle

Non potremo sapere dove si trovi il file poichè nella variabile GET abbiamo solo il suo nome e non stiamo utilizzando un database.
Ricordi che ti sarebbe tornato molto utile il metodo YCFile::find()? Ecco, questo è un caso in cui lo utilizzeremo:

require_once('ycfile.php');
define('DS', DIRECTORY_SEPARATOR);

//Questa variabile contiene il percorso relativo alla cartella dove sono contenuti i file.
$basePath = 'uploads' . DS;
$fileName = $_GET['file'];

//Il nome del file viene decodificato.
//Questo passaggio è necessario nel caso non si voglia lasciare il nome del file in chiaro
//nell'URL e quindi lo si passa a questa pagina criptato con base64_encode().
$fileNameDecoded = base64_decode($fileName);

$searchIn = array('downloads', 'articles', 'galleries');

//Cerchiamo il file nelle cartelle
foreach($searchIn as $thePath) {
	$filePath = false;
	$found = YCFile::find($fileNameDecoded, $basePath . $thePath . DS);
	if($found) {
		$filePath = $found;
		break;
	}
}

if(!$filePath) {
	die('Il file non esiste.');
} else {
	if(!YCFile::download($filePath)) {
		die('Impossibile scaricare il file.');
	}

	exit(0);
}

Semplice no?
Specifichiamo in quali cartelle cercare il file, se esiste in una di queste, viene inviato al browser per il download, altrimenti blocco lo script e invio un messaggio d’errore.

Conclusioni

Questo è sicuramente uno dei metodi più sicuri e funzionali per far scaricare i file agli utenti.
Come vedi non è neanche complicato e funziona tramite una semplice pagina, indipendentemente da quante cartelle abbiamo.

Puoi utilizzare entrambi i metodi contemporaneamente. Nel file di esempio ci sarà il file .htaccess che blocca gli accessi diretti ai file. Se però permetti l’accesso tramite la pagina force-download.php tutto funziona correttamente come noi ci aspettiamo!

Download
Tag: , ,

L'autore

Sviluppo per il web da alcuni anni. Ultimamente mi sono specializzato in WordPress tramite Your Inspiration, con il quale attualmente lavoro nell'area di supporto clienti e sviluppo temi e plugin per WP.

Sito web dell'autore | Altri articoli scritti da

Articoli correlati

Potresti essere interessato anche ai seguenti articoli:

24 commenti

Trackback e pingback

  1. I migliori post della settimana #125 | Web Developer Freelance / Web Designer / SEO Specialist Freelance :: EmaWebDesign
    [...] 04) Come gestire i file: il download [...]
  2. Articoli settimana 19/02/2012 | Saverio Gravagnola
    [...] gestire i file: il download (Your Inspiration [...]
  3. Come gestire i file: le basi (parte 2) | Your Inspiration Web
    [...] Come gestire i file: il download Vediamo come effettuare il download dei file in sicurezza per te e per…
  4. Come gestire i file: le basi (Parte 1) | Your Inspiration Web
    [...] Come gestire i file: il download Vediamo come effettuare il download dei file in sicurezza per te e per…
  5. Come gestire i file: l’upload | Your Inspiration Web
    [...] Come gestire i file: il download Vediamo come effettuare il download dei file in sicurezza per te e per…