[php] controllo su estensione file

tunix

Nuovo Utente
7 Giu 2018
2
0
1
Ciao a tutti,

Vorrei chiedervi aiuto nel creare un controllo dell'estensione in uno script per allegare file pdf tramite un form html...
Vorrei anche valutare la possibilità di limitare la grandezza dell'allegato.

Ho già creato uno script javascript che mi esegue dei controlli sull'inserimento di un campo mail e della presenza dell'allegato.

Mentre questo script l'ho già copiato e modificato a mio piacere da un articolo di Massimiliano Bossi

Se serve altro, fornisco dettagli, grazie :)

PHP:
<?php
// Recupero il valore dei campi del form
$destinatario = 'XX@xx.xx';
$mittente = $_POST['mittente'];
$oggetto = "Mail Automatica inviata dal form" ;
$messaggio = "In allegato";

// Valorizzo le variabili relative all'allegato
$allegato = $_FILES['allegato']['tmp_name'];
$allegato_type = $_FILES['allegato']['type'];
$allegato_name = $_FILES['allegato']['name'];

// Creo altre due variabili ad uso interno
$headers = "From: " . $mittente;
$msg = "";

// Verifico se il file è stato caricato correttamente via HTTP
// In caso affermativo proseguo nel lavoro...
if (is_uploaded_file($allegato))
{
  // Apro e leggo il file allegato
  $file = fopen($allegato,'rb');
  $data = fread($file, filesize($allegato));
  fclose($file);

  // Adatto il file al formato MIME base64 usando base64_encode
  $data = chunk_split(base64_encode($data));

  // Genero il "separatore"
  // Serve per dividere, appunto, le varie parti del messaggio.
  // Nel nostro caso separerà la parte testuale dall'allegato
  $semi_rand = md5(time());
  $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
 
  // Aggiungo le intestazioni necessarie per l'allegato
  $headers .= "\nMIME-Version: 1.0\n";
  $headers .= "Content-Type: multipart/mixed;\n";
  $headers .= " boundary=\"{$mime_boundary}\"";

  // Definisco il tipo di messaggio (MIME/multi-part)
  $msg .= "This is a multi-part message in MIME format.\n\n";

  // Metto il separatore
  $msg .= "--{$mime_boundary}\n";

  // Questa è la parte "testuale" del messaggio
  $msg .= "Content-Type: text/plain; charset=\"iso-8859-1\"\n";
  $msg .= "Content-Transfer-Encoding: 7bit\n\n";
  $msg .= $messaggio . "\n\n";

  // Metto il separatore
  $msg .= "--{$mime_boundary}\n";

  // Aggiungo l'allegato al messaggio
  $msg .= "Content-Disposition: attachment; filename=\"{$allegato_name}\"\n";
  $msg .= "Content-Transfer-Encoding: base64\n\n";
  $msg .= $data . "\n\n";

  // chiudo con il separatore
  $msg .= "--{$mime_boundary}--\n";
}

// Invio la mail
if (mail($destinatario, $oggetto, $msg, $headers))
{
header("xxxx"); //Riapro la pag. iniziale
}else{
  echo "<p>Errore!</p>";
}
?>
 

MarcoGrazia

Utente Attivo
15 Dic 2009
764
15
18
58
Udine
www.stilisticamente.com
Devi allegare un file ad un'email inviata da un form pubblico? Non si fa! Sai lo spam e i tentativi di inviare worm tramite il tuo sito?
Tanto sei stato tu mica io :D

Ma non si fa per un motivo ben preciso, verificare il tipo di file inviato è virtualmente impossibile, ci sono dei metodi ma sono aggirabili.
Per esempio già l'HTML5 permette di inserire nell'input di file l'attributo accept a cui passare il mime type del file da caricare, ma se io utilizzo un browser datato o che comunque non riconosce l'html5 ecco aggirato l'ostacolo.
Via PHP è molto dura leggere il tipo di file; io una volta arrivato il file tramite l'array $_FILE ne leggo i primi 6 caratteri, dove c'è solitamente l'indirizzo del tipo di file alla ricerca dei caratteri che me lo identificano, ma sai che ci vuole a sovrascriverli?
 

tunix

Nuovo Utente
7 Giu 2018
2
0
1
Intanto grazie per la risposta :)

Purtroppo certe cose non le partorisce la mia testa, ma ahimè, devo cercare di eseguirle...
È già qualcosa farle arrivare ad un alias e poi ci pensa l'antivirus (non delle balle) ancor prima che arrivi al client, però mi serve quanto meno cercare di limitare il tipo di file al solo pdf.
Inserisco un captcha per aiutare..

Se non posso farla da php, posso forse in js?

Codice:
    x = document.getElementById("allegato").value;
    if (x == "")
    errore = "Non puoi inviare senza allegare."
 

macus_adi

Utente Attivo
5 Dic 2017
1.266
82
48
IT/SW
$_FILE ne leggo i primi 6 caratteri
Cosa leggi?
Esistono librerie atte a leggere preventivamente il file, vedi zend framework, oltre che analizzare l'estensione (banalmente raggirabile) ne leggi il contenuto ed il relativo type corretto....
Hai mai provato ad inviare una mail con una dll dentro, magari anche zippata? Bene... Come fa google ad impedire l'invio?
La logica è la stessa... solo che applicata all'upload...

Salut!
 

MarcoGrazia

Utente Attivo
15 Dic 2009
764
15
18
58
Udine
www.stilisticamente.com
il tipo mime, eccoti la funzione che lo legge dentro il file e ritorna il tipo mime, oppure false se non lo trova:
PHP:
function minimime($fname) {
    $fh = fopen($fname, 'rb');
    if ($fh) {
        $bytes6 = fread($fh, 6);
        fclose($fh);
        if ($bytes6 === false) return false;
        //    IMG
        if (substr($bytes6, 0, 3) == "\xff\xd8\xff") return 'image/jpeg';
        if (substr($bytes6, 0, 2) == "\x42\x4D") return 'image/x-windows-bmp';
        if ($bytes6    == "\x89PNG\x0d\x0a" || $bytes6    == "\x89\x50\x4E\x47\x0D\x0A") return 'image/png';
        if ($bytes6    == "\x47\x49\x46\x38\x37\x61" || $bytes6 == "\x47\x49\x46\x38\x39\x61") return 'image/gif';
        if ($bytes6    == "GIF87a" || $bytes6 == "GIF89a") return 'image/gif';
        if (substr($bytes6, 0, 4) == "\x49\x49\x2A\x00" || substr($bytes6, 0, 4) =="\x4D\x4D\x00\x2A") return 'image/tiff';
        //    PDF
        if ($bytes6 == "%PDF" || substr($bytes6, 0, 4) == "\x25\x50\x44\x46") return 'application/pdf';
        //    ZIP, RAR file or 7z
        if (substr($bytes6, 0, 2) == "PK" || substr($bytes6, 0, 2) == "\x50\x4B") return 'application/zip';
        if ($bytes6 == "\x52\x61\x72\x21\x1A\x07") return 'application/x-rar-compressed';
        if ($bytes6 == "\x75\x73\x74\x61\x72") return 'application/x-tar';
        if (substr($bytes6, 0, 2) == "7z" || $bytes6 == "\x37\x7A\xBC\xAF\x27\x1C") return 'application/x-7z-compressed';
        //    ELSE
        return 'application/octet-stream';    //    Generic file
    }
    return false;
}    //    minimime()
Il tipo di mime si trova sempre nei primi 6 caratteri del file, ovvio che si può sempre modificare e il file funziona lo stesso.
Una DLL porta sempre nei primi 4 caratteri il codice esadecimale 4D 5A , in ASCII: MZ.
Esistono librerie atte a leggere preventivamente il file, vedi zend framework, oltre che analizzare l'estensione (banalmente raggirabile) ne leggi il contenuto ed il relativo type corretto....
Ovvio che ne esistono, ma non sempre uso un intero framework per estrapolare un solo dato, persino PHP non ha solo la libreria che lo estrapola dall'array $_FILE, per esempio le FINFO sono molto più performanti ma non sempre sono disponibili dato che vannno abilitate.
Hai mai provato ad inviare una mail con una dll dentro, magari anche zippata? Bene... Come fa google ad impedire l'invio?
La logica è la stessa... solo che applicata all'upload...
Ovvio ma che discorso è, ognuno usa o sviluppa le librerie che ritiene necessarie; GMail è un'applicazione, come altre, che permette di inviare e ricevere email, ma a quello serve e mi aspetto che funzioni in modo egregio, ma dietro Google ci sono migliaglia di sviluppatori, se io devo realizzare un sito non ho ne il loro background ne i loro sviluppatori.
Posso solo immaginare come fanno loro e pensare a come farei io, ma tutto dipende.
Se devo sviluppare un sito in cui c'è un form per inviare email, cerco di renderlo più semplice possibile, proprio per non cacciarmi in problemi ulteriori. A meno che ovviamente per lavoro non mi chiedano di più, ma a quel punto non dovrei essere io da solo a decidere sul da farsi, ma devo spiegare quale sarà la strategia da seguire.
Inviare un file da form pubblico è sempre un pericolo ed una libreria preconfezionata non da mai la sicurezza al 100% uno se è obbligato ci prova, se usi zend framework o altro per il lavoro usi ovviamente le sue funzioni, non reinventi la ruota avendola già a disposizione, se non è questo il tuo caso ti tocca inventare qualche cosa, ne va non solo della sicurezza del sito e di chi riceve le email, ma anche della credibilità dell'azienda che l'ha ordinato e soprattutto della credibilità di chi lo ha sviluppato, capro espiatorio per ogni problema che si possa verificare.
Buonissima giornata a te.

@tunix in javascript non è che serva per la sicurezza, o meglio, se impedisci l'invio del form se non è attivo il javascript può anche andare, ma è sempre meglio utilizzare un sistema lato server per la sicurezza.
 
Ultima modifica:

macus_adi

Utente Attivo
5 Dic 2017
1.266
82
48
IT/SW
Ciao @MarcoGrazia la tua funzione che funziona egregiamente è sintetizzabile con 1 (una) linea di codice, vedi qui.
Ovvio ma che discorso è, ognuno usa o sviluppa le librerie che ritiene necessarie;
Bhè qui il discorso è uno: La ruota è stata inventata, si sviluppa per cercare di minimizzare codice, e comunque senza andare lontano e parlare di framework blasonati si potrebbe utilizzare la Classe di Codeigniter (https://www.codeigniter.com/userguide3/libraries/file_uploading.html), che fa esattamente ciò che serve...

Buona giornata, buon code editing e buono studio!
 

MarcoGrazia

Utente Attivo
15 Dic 2009
764
15
18
58
Udine
www.stilisticamente.com
Concordo con te sull'utilizzo di una classe del genere se devi controllare l'aupload, ma se ti serve il solo controllo del file è una procedura costosa in termini di risorse.
Per questo ho affrontato il problema da un punto di vista radicale; non voglio reinventare la ruota, ma nemmeno mettere una ruota da 17 pollici in una 500 ;-)
 

macus_adi

Utente Attivo
5 Dic 2017
1.266
82
48
IT/SW
@MarcoGrazia Punti di vista... un Controller che pesa poco più di 17k non credo (ed infatti non lo è) sia molto esoso di risorse. Il cerchio da 17 sarebbe da prevedere per future scalabilità dell'applicazione...
Certo il discorso prende una piega ben diversa (e concordo pienamente con te) quando si analizzano i seguenti punti:
  1. Budget
  2. Tempi
  3. Compliance
  4. Macchina di produzione
  5. Servizi
  6. etc etc...
Mi fermo, la lista è lunga!

Controllando meglio la docs di PHP, le librerie inerenti alla manipolazione dei file sono rese disponibili di default da PHP.

@tunix Affrontando il discorso a modi servizi si potrebbe prevedere la validazione del media attraverso un qualsiasi servizio atto alla verifica dello stesso.