[PHP] upload di file in cartella e sua sicurezza

  • Creatore Discussione Creatore Discussione VAik
  • Data di inizio Data di inizio
@borgo italia
un paio di esempi,

caso 1

Upload : PNG.txt
Type : text/plain ------------------------------ restituito dal browser ie11
Size (kB) : 42.76953125
Stored in : C:\Windows\Temp\php87A2.tmp
target folder : C:/Web_Sites/__Test/PHP/TEST/_xnotar/
target file : C:/Web_Sites/__Test/PHP/TEST/_xnotar/PNG.txt
MIME TYPE : image/png -------------------------- restituito dalla funzione php
Il file -- PNG.txt -- è stato caricato correttamente.
( che sia stato caricato come txt non è proprio giusto e sarà necessario decidere cosa fare )

caso 2

Upload : txt.jpg
Type : text/plain ------------------------------ restituito dal browser ie11
Size (kB) : 4.3974609375
Stored in : C:\Windows\Temp\php2A67.tmp
target folder : C:/Web_Sites/__Test/PHP/TEST/_xnotar/
target file : C:/Web_Sites/__Test/PHP/TEST/_xnotar/txt.jpg
MIME TYPE : text/x-php -------------------------- restituito dalla funzione php
ERRORE : Il file non è del tipo corretto.

ma sembra che il browser non sia perfetto ....
 
ciao @marino51
guarda che
$mime_type_tmp = mime_content_type($_FILES["fileToUpload"]["tmp_name"]);
è php e dove altrimenti va a leggere le info?
un pinco_pallo qualsiasi per usare la pagina per allegare i documenti usando il sito di xnotar deve usare un bw, quindi a php legge le info che quel bw trasmette
 
ciao Gianni (@borgo italia),
ti seguo poco, e quindi provo a spiegare il mio pensiero,

obiettivo, identificare il tipo di file non usando l'informazione proveniente dal browser
PHP:
$file_type = strtolower($_FILES['fileToUpload']['type']); // mime type dal browser

di conseguenza, ho usato nello script l'informazione restituita da "finfo" o in alternativa da "mime_content_type"
per questa seconda, il manuale recita, "Detect MIME Content-type for a file"
non ha quindi nessuna attinenza con l'upload ovvero non recupera l'informazione del browser ma la determina analizzando il file (vedi la differenza negli esempi in un post appena sopra)

detto ciò, sarà probabilmente necessario aggiungere anche il controllo sull'estensione del file per evitare che qualche burlone si diverta a caricare "stupidate" modificando le estensioni, ma ... passo successivo, per ora è sufficiente che le due funzioni php restituiscano correttamente il risultato del loro lavoro su un sistema Linux (del quale non ho disponibilità e quindi devo attendere il risultato delle prove)
 
Ultima modifica:
Ho fatto alcune prove con l'ultimo script che hai postato.
I jpg, vengono correttamente caricati da tutti e tre i browser, anche se gli cambio estensione esempio LOGO_WEB.jpg se lo cambio in .php lo carica.
Il browser lo invia come application/x-php ma php riconosce il mime-type come image/jpeg
Upload : LOGO_WEB.php
Type : application/x-php
Size (kB) : 105.3759765625
Stored in : /tmp/phpXSbkeO
target folder : models/
target file : models/LOGO_WEB.php
MIME TYPE : image/jpeg
Il file -- LOGO_WEB.php -- è stato caricato correttamente.
mentre il test2.php camuffato in .jpg non viene caricato perché riconosciuto da php come text/plain
Upload : test2.jpg
Type : image/jpeg
Size (kB) : 0.81640625
Stored in : /tmp/phpnx0VmB
target folder : models/
target file : models/test2.jpg
MIME TYPE : text/plain
ERRORE : Il file non è del tipo corretto.
Per quanto riguarda i file stl, non vengono caricati. Php riconosce application/octet-stream
Upload : cubo.stl
Type : application/sla
Size (kB) : 0.66796875
Stored in : /tmp/phpW7OFSN
target folder : models/
target file : models/cubo.stl
MIME TYPE : application/octet-stream
ERRORE : Il file non è del tipo corretto.
per quanto riguarda gli spazi contenuti nei nomi dei file, vengono correttamente identificati e viene mostrato l'errore prima della verifica del MIME
Upload : LOGO WEB.jpg
Type : image/jpeg
Size (kB) : 105.3759765625
Stored in : /tmp/phpn9idd6
target folder : models/
target file : models/LOGO WEB.jpg
ERRORE : Il nome del file intercalato da spazi non è accettato. Rinomina il tuo file!
 
ciao
per il controllo anche dell'estenzione sono d'accordo infatti avevo messo giu uno schema
PHP:
<?php
if(isset($_POST['invia'])){
    echo "<pre>";
    $mime_type_tmp = mime_content_type($_FILES["fileToUpload"]["tmp_name"]);
    var_dump($mime_type_tmp);
    $mime_type = mime_content_type($_FILES["fileToUpload"]["tmp_name"]);
    var_dump($mime_type);
    $nome_file=$_FILES["fileToUpload"]["name"];
    var_dump($nome_file);
    $est=strtolower(pathinfo($_FILES["fileToUpload"]["name"], PATHINFO_EXTENSION));
    var_dump($est);
/*   output
string(13) "image/vnd.dwg"
string(13) "image/vnd.dwg"
string(9) "bagno.dwg"
string(3) "dwg"
bagno.dwg tipo consentito

string(9) "image/png"
string(9) "image/png"
string(15) "logoscritta.png"
string(3) "png"
logoscritta.png tipo consentito

*/
    $mime=array('image/vnd.dwg','image/png');//ecc.....
    $tipo=array('dwg','png');//ecc.....
    if(in_array($mime_type_tmp, $mime) && in_array($est, $tipo)){
        echo "$nome_file tipo consentito<br>";
    }else{
        echo "$nome_file tipo NON consentito<br>";
    }
}
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
<input name="fileToUpload" type="file">
<input name="invia" type="submit">
</form>
per il primo non molto in quanto tu leggi i vari $_FILES["fileToUpload"][".........."] che comunque sono filtrati/richiamati dal bw
 
per il primo non molto in quanto tu leggi i vari $_FILES["fileToUpload"][".........."] che comunque sono filtrati/richiamati dal bw[/QUOTE]
intendiamoci,
viene letto il file caricato, nello specifico il suo contenuto, nulla a che fare con il browser

se poi intendi $_FILE, si andranno inserite le "validation"
 
Ultima modifica:
ciao
dimenticavo, i controlli possono diventare tre
PHP:
//.....
$type_tmp = $_FILES["fileToUpload"]["type"];
$mime_type_tmp = mime_content_type($_FILES["fileToUpload"]["tmp_name"]);
$est=strtolower(pathinfo($_FILES["fileToUpload"]["name"], PATHINFO_EXTENSION));
//costruire tre array e metterli nell'if
if(in_array($type_tmp, $type) && in_array($mime_type_tmp, $mime) && in_array($est, $tipo)){
    //eccc...   
}
//....
 
LOGO_WEB.php, non ho altre idee se non controllare l'estensione e rifiutare il file
si potrebbe modificare l'array "allow..." mettendo l'estensione come chiave ed il tipo atteso come valore in modo da fare il controllo incrociato

test2.jpg, ok è ciò che si vuole

cubo.stl, purtroppo sembra che le funzioni php non riconoscano il file, "application/octet-stream" è un valore "generico"
potrebbe avere un senso l'estensione dell'array come detto sopra ma comunque è parzialmente una "porta aperta"
credo sia inutile usare la definizione del browser che forse la ricava dall'estensione

Il nome del file intercalato da spazi non è accettato. Rinomina il tuo file!, credo vada bene lasciare il controllo

se concordi provo ad inserire la variazione sull'array
 
Beh se anche qualcuno caricasse un jpg camuffato da php, per la sicurezza non credo sia un problema (almeno credo) con un file jpg cosa ci potrebbero fare?
 
Si ma per quello anche in un'immagine vera potrebbero nascondere del codice maligno
ma le funzioni php dovrebbero accorgersi

il codice che segue lega l'estensione con il mime di php, secondo me, aumentando la sicurezza, fai la prova con quelli già definiti, poi andranno aggiunti le altre estensioni con il relativo mime restituito dalle funzioni di php
PHP:
function GestisciUpload($file_path = "models/")
{
    $allowed_type = array
    (
        'stl'   => 'application/octet-stream',
        'gif'   => 'image/gif',
        'jpeg'  => 'image/jpeg',
        'pjpeg' => 'image/pjpeg',
        'png'   => 'image/png',
//        'application/acad',
//        'application/dxf',
//        'application/iges',
//        'application/sla',
//        'application/vnd.ms-pki.stl',
//        'application/x-navistyle',
//        'image/vnd.dwg',
//        'image/x-dwg',
//        'model/iges',
//        'x-world/x-3dmf',
    );

    $UploadErrors = array
    (
        'There is no error, the file uploaded with success',
        'The uploaded file exceeds the upload_max_filesize directive in php.ini',
        'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
        'The uploaded file was only partially uploaded',
        'No file was uploaded',
        'Missing a temporary folder',
        'Failed to write file to disk',
        'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help',
    );

    $err = $_FILES['fileToUpload']['error'];
    if ($err > 0 )
    {
        echo "ERRORE : ".( $err < 8 ? $UploadErrors[$err] : $err )."<br /><br />";
        return false;
    }

    // controllo se esiste la cartella di destinazione
    if ( !is_dir($file_path) )
    {
        echo "ERRORE : La cartella di destinazione non esiste<br /><br />";
        return false;
    }

    $file_name = $_FILES["fileToUpload"]["name"];             // nome originale del file ( Linux Unix : attenzione maiuscole e minuscole )
    $file_ext  = pathinfo($file_name, PATHINFO_EXTENSION);    // estensione del file caricato
    $file_type = strtolower($_FILES['fileToUpload']['type']); // mime type dal browser
    $file_size = $_FILES['fileToUpload']['size'] / 1024;      // dimensione in kB
    $file_temp = $_FILES['fileToUpload']['tmp_name'];         // file temporaneo, path e nome assegnato dall'upload
    $file      = $file_path.$file_name;                       // path di destinazione e nome originale del file

    // visualizzazioni da eliminare (commentare) in produzione
    echo "Upload : "        .$file_name."<br />";
    echo "extension : "     .$file_ext ."<br />";
    echo "Type : "          .$file_type."<br />";
    echo "Size (kB) : "     .$file_size."<br />";
    echo "Stored in : "     .$file_temp."<br />";
    echo "target folder : " .$file_path."<br />";
    echo "target file : "   .$file     ."<br />";

    // evito nomi di files con spazi intercalati
    if ( substr_count($file_name, ' ') )
    {
        echo "ERRORE : Il nome del file intercalato da spazi non &egrave; accettato. Rinomina il tuo file!<br /><br />";
        return false;
    }

    // controllo se posso ottenere "mime_type"
    if ( class_exists('finfo') )
    {
        $mime_type = ( new finfo(FILEINFO_MIME_TYPE) )->buffer( file_get_contents($file_temp) );
    }
    else if ( function_exists('mime_content_type') )
    {
        $mime_type = mime_content_type($file_temp);
    }
    else
    {
        echo "ERRORE : non posso ottenere 'mime_type' mancano gli strumenti php<br /><br />";
        return false;
    }
    $mime_type = strtolower($mime_type);     // per avere facilità nella gestione dell'array "allowed_type" ammesso ci siano maiuscole
    echo "MIME TYPE : ".$mime_type."<br />";

    // controllo se è accettabile
    if ( empty($allowed_type[$file_ext]) or $mime_type != $allowed_type[$file_ext] )
    {
        echo "ERRORE : Il file non &egrave; del tipo corretto.<br /><br />";
        return false;
    }

    // controllo la dimensione
    if ($file_size == 0)
    {
        echo "ERRORE : Il file &egrave; vuoto.<br /><br />";
        return false;
    }
    if ($file_size > 5000)
    {
        echo "ERRORE : Il file ha una dimensione troppo grande. Carica un file che non superi i 5 Mb.<br /><br />";
        return false;
    }

    // controllo se esiste lo stesso nome
    if ( file_exists($file) )
    {
        echo "ERRORE : Il nome scelto per il file esiste gi&agrave;. Rinomina il tuo file!<br /><br />";
        return false;
    }

    // sposto il file nella destinazione
    if ( !move_uploaded_file($file_temp, $file) )
    {
        echo "ERRORE : Lo spostamento del file nella destinazione non &egrave; riuscito"."<br /><br />";
        return false;
    }

    // controllo se il file é arrivato a destinazione
    if (!file_exists($file))
    {
        echo "ERRORE : Il file non &egrave; arrivato a destinazione<br /><br />";
        return false;
    }

    // non ho altro da fare
    echo "Il file -- ".$file_name." -- &egrave; stato caricato correttamente.";
    return true;
}
 
Ultima modifica di un moderatore:
per pulire la cartella temporanea, in caso di errore, puoi aggiungere "unlink", come sotto riportato
PHP:
    $uploadOk = GestisciUpload($cartella);
    if ( $uploadOk === false ) unlink($_FILES['fileToUpload']['tmp_name']);

    // qui va gestito il comportamento successivo
 
Ho capito, praticamente gli fai controllare che l'estensione inviata sia effettivamente del MIME definito come tale, però non capisco perché il stl che ha un mime "application/sla" da php non venga riconosciuto e si debba mettere application/octet-stream ?
 
ciao
dimenticavo, i controlli possono diventare tre
PHP:
//.....
$type_tmp = $_FILES["fileToUpload"]["type"];
$mime_type_tmp = mime_content_type($_FILES["fileToUpload"]["tmp_name"]);
$est=strtolower(pathinfo($_FILES["fileToUpload"]["name"], PATHINFO_EXTENSION));
//costruire tre array e metterli nell'if
if(in_array($type_tmp, $type) && in_array($mime_type_tmp, $mime) && in_array($est, $tipo)){
    //eccc...  
}
//....
già provato non funziona, può essere imbrogliato
 
Ho capito, praticamente gli fai controllare che l'estensione inviata sia effettivamente del MIME definito come tale, però non capisco perché il stl che ha un mime "application/sla" da php non venga riconosciuto e si debba mettere application/octet-stream ?
io credo che il browser possa fare il furbo, restituendo il tipo dall'estensione, se rinomini un jpg in txt, il browser ti dice che tipo = testo ....
mentre php ti restituisce il tipo corretto

suppongo che php non abbia tutte le "definizioni" per interpretare la miriade di possibili file, ma gestisca solo i più comuni

ma incrociando estensione e tipo possiamo limitare i danni
 
per favore inserisci la riga in mezzo, usa le altre come riferimento per la posizione
PHP:
    // controllo se è accettabile
    $file_ext = strtolower($file_ext);
    if ( empty($allowed_type[$file_ext]) or $mime_type != $allowed_type[$file_ext] )
 

Discussioni simili