Mysql
 sql >> Database >  >> RDS >> Mysql

php come collegare un file dal file server a quelle informazioni dal database

Ho pensato di scrivere una breve (per me questa è breve) "risposta" solo per riassumere i miei punti.

Alcune "migliori pratiche" durante la creazione di un sistema di archiviazione di file. L'archiviazione di file è una categoria ampia, quindi il tuo chilometraggio può variare per alcuni di questi. Prendili solo come suggerimento di ciò che ho trovato funziona bene.

Nomi di file Non archiviare il file con il nome assegnato da un utente finale. Possono e useranno tutti i tipi di personaggi schifosi che renderanno la tua vita infelice. Alcuni possono essere pessimi come ' virgolette singole, che praticamente su Linux rendono impossibile leggere o addirittura eliminare il file (direttamente). Alcune cose possono sembrare semplici come uno spazio ma a seconda di dove lo usi e del sistema operativo sul tuo server potresti finire con one%20two.txt o one+two.txt o one two.txt che possono creare o meno tutti i tipi di problemi nei tuoi link.

La cosa migliore da fare è creare un hash, qualcosa come sha1 può essere semplice come {user_id}{orgianl_name} Il nome utente rende meno probabili le collisioni con i nomi di file di altri utenti.

Preferisco fare file_hash('sha1', $contents) in questo modo se qualcuno carica lo stesso file più di una volta puoi prenderlo (i contenuti sono gli stessi l'hash è lo stesso). Ma se prevedi di avere file di grandi dimensioni, potresti voler eseguire alcuni benchmark su di esso per vedere che tipo di prestazioni ha. Gestisco principalmente file di piccole dimensioni, quindi funziona bene per quello.-nota- che con il timestamp il file può ancora essere salvato perché il nome completo è diverso, ma lo rende abbastanza facile da vedere e può essere verificato nel database.

Indipendentemente da quello che fai, lo anteporrei con un timestamp time().'-'.$filename . Questa è un'informazione utile da avere, perché è l'ora assoluta in cui è stato creato il file.

Per quanto riguarda il nome che un utente dà al file. Basta memorizzarlo nel record del database. In questo modo puoi mostrare loro il nome che si aspettano, ma usa un nome che sai essere sempre sicuro per i link.

$filename ='qualche schifo^ fileane.jpg';

$ext = strrchr($filename, '.');

echo "\nExt: {$ext}\n";

$hash = sha1('some crapy^ fileane.jpg');

echo "Hash: {$hash}\n";

$time = time();

echo "Timestamp: {$time}\n";

$hashname = $time.'-'.$hash.$ext;

echo "Hashname: $hashname\n";

Uscite

Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg

Puoi provarlo qui

Percorsi non memorizzare mai il percorso completo del file. Tutto ciò di cui hai bisogno nel database è l'hash dalla creazione del nome hash. Il percorso "root" della cartella in cui è archiviato il file deve essere eseguito in PHP. Questo ha diversi vantaggi.

  • impedisce il trasferimento di directory. Perché non stai passando nessuna parte del percorso intorno a te non devi preoccuparti tanto che qualcuno ti infili un \..\.. lì e andare in posti non dovrebbero. Un pessimo esempio di questo potrebbe essere qualcuno che sovrascrive un .htpassword file caricando un file chiamato quello con la directory trasversale al suo interno.
  • Ha collegamenti dall'aspetto più uniforme, dimensioni uniformi, set di caratteri uniforme.

https://en.wikipedia.org/wiki/Directory_traversal_attack

  • Manutenzione. Cambiano i percorsi, cambiano i server. Richieste sul cambiamento del tuo sistema. Se hai bisogno di riposizionare quei file, ma hai memorizzato il loro percorso completo assoluto nel DB, ti sei bloccato incollando tutto insieme a symlinks o aggiornando tutti i tuoi record.

Ci sono alcune eccezioni a questo. Se vuoi salvarli in una cartella mensile o per nome utente. Potresti salvare quella parte del percorso, in un campo separato. Ma anche in quel caso, potresti costruirlo dinamicamente in base ai dati salvati nel record. Ho scoperto che è meglio salvare il minor numero possibile di informazioni sul percorso. E creano una configurazione o una costante che puoi utilizzare in tutti i posti in cui devi inserire il percorso del file.

Anche il path e il link sono molto diversi, quindi salvando solo il nome puoi collegarlo da qualsiasi pagina PHP desideri senza dover sottrarre dati dal percorso. Ho sempre trovato più facile aggiungere al nome del file e poi sottrarre da un percorso.

Banca dati (solo alcuni suggerimenti, l'utilizzo può variare )Come sempre con i dati chiediti, chi, cosa, dove, quando

  • id - int incremento automatico della chiave primaria
  • id_utente - int chiave esterna, chi l'ha caricato
  • hash - char[40] *sha1*, unique cosa l'hash
  • nome hash - varchar {timestampl}-{hash}.{ext} dove il nome del file sul disco rigido
  • nome file - varchar il nome originale fornito dall'utente, in questo modo possiamo mostrare loro il nome che si aspettano (se è importante)
  • stato - enum[public,private,deleted,pending.. etc] stato del file, a seconda del tuo caso d'uso, potresti dover rivedere i file, o forse alcuni sono privati ​​solo l'utente può vederli, forse alcuni sono pubblici ecc.
  • stato_data - timestamp|datetime momento in cui lo stato è stato modificato.
  • crea_data - timestamp|datetime quando al momento della creazione del file, è preferibile un timestamp in quanto semplifica alcune cose, ma in tal caso dovrebbe essere lo stesso timestamp utilizzato nell'hashname.
  • digitare - varchar - tipo mime, può essere utile per impostare il tipo mime durante il download, ecc.

Se prevedi che utenti diversi carichino lo stesso file e utilizzi file_hash puoi fare l'hash campo un indice univoco combinato di user_id e l'hash in questo modo andrebbe in conflitto solo se lo stesso utente caricasse lo stesso file. Potresti anche farlo in base al timestamp e all'hash, a seconda delle tue esigenze.

Queste sono le cose di base a cui potrei pensare, questo non è un assoluto, solo alcuni campi che pensavo sarebbero stati utili.

È utile avere l'hash da solo, se lo memorizzi da solo puoi salvarlo in un CHAR(40) per sha1 (occupa meno spazio nel DB quindi VARCHAR ) e imposta le regole di confronto su UTF8_bin che è binario. Ciò rende le ricerche su di esso con distinzione tra maiuscole e minuscole. Sebbene ci siano poche possibilità di una collisione di hash, questo aggiunge solo un po' più di protezione perché gli hash sono lettere maiuscole e minuscole.

Puoi sempre creare il hashname al volo se memorizzi l'estensione e il timestamp separati. Se ti ritrovi a creare cose più e più volte, potresti semplicemente volerlo archiviare nel DB per semplificare il lavoro in PHP.

Mi piace semplicemente mettere l'hash nel link, nessuna estensione, niente, quindi i miei link hanno questo aspetto.

http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea

Vero semplice, vero generico, sicuro negli URL sempre della stessa dimensione ecc.

Il hashname per questo "file" sarebbe così

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg

Se hai conflitti con lo stesso file e un utente diverso (che ho menzionato sopra). Puoi sempre aggiungere la parte timestamp nel link, user_id o entrambi. Se usi user_id, potrebbe essere utile riempirlo a sinistra con zeri. Ad esempio, alcuni utenti potrebbero avere ID:1 e alcuni potrebbero essere ID:234 quindi puoi lasciare il pad in 4 posizioni e renderle 0001 e 0234 . Quindi aggiungilo all'hash, che è quasi impercettibile:

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg

La cosa importante qui è che perché sha1 è sempre 40 e l'id è sempre 4 possiamo separare i due accuratamente e facilmente. E in questo modo, puoi ancora cercarlo in modo univoco. Ci sono molte opzioni diverse, ma molto dipende dalle tue esigenze.

Accesso Come il download. Dovresti sempre emettere il file con PHP, non dare loro l'accesso diretto al file. Il modo migliore è archiviare i file al di fuori della webroot (sopra il public_html o www cartella ). Quindi in PHP puoi impostare le intestazioni sul tipo corretto e in pratica leggere il file. Funziona praticamente per tutto tranne che per i video. Non gestisco video, quindi questo è un argomento al di fuori della mia esperienza. Ma trovo che sia meglio pensarci perché tutti i dati dei file sono testo, sono le intestazioni che trasformano quel testo in un'immagine o un file excel o un pdf.

Il grande vantaggio di non dare loro l'accesso diretto al file è se hai un sito di appartenenza, o non vuoi che i tuoi contenuti siano accessibili senza un login, puoi facilmente controllare in PHP se hanno effettuato l'accesso prima di fornire loro il contenuto. E, poiché il file è al di fuori della webroot, non possono accedervi in ​​nessun altro modo.

La cosa più importante è scegliere qualcosa di coerente, che sia comunque sufficientemente flessibile da gestire tutte le tue esigenze.

Sono sicuro di poterne inventare di più, ma se hai qualche suggerimento sentiti libero di commentare.

FLUSSO DEL PROCESSO DI BASE

  1. L'utente invia il modulo (enctype="multipart/form-data" )

https://www.w3schools.com/tags/att_form_enctype.asp

  1. Il server riceve il post dal modulo Super Globals $_POST e il $_FILES

http://php.net/manual/en/reserved.variables.files .php

$_FILES = [
 'fieldname' => [
        'name' => "MyFile.txt" // (comes from the browser, so treat as tainted)
        'type' => "text/plain" //  (not sure where it gets this from - assume the browser, so treat as tainted)
        'tmp_name' => "/tmp/php/php1h4j1o" // (could be anywhere on your system, depending on your config settings, but the user has no control, so this isn't tainted)
        'error' => "0" //UPLOAD_ERR_OK  (= 0)
        'size' => "123" //   (the size in bytes)
    ]
 ];
  1. Verifica la presenza di errori if(!$_FILES['fielname']['error'])

  2. Disinfetta il nome visualizzato $filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");

  3. Salva file, crea record DB ( PSUDO-CODE )

In questo modo:

 $path = __DIR__.'/uploads/'; //for exmaple

$time = time();
$hash = hash_file('sha1',$_FILES['fielname']['tmp_name']);
$type = $_FILES['fielname']['type'];
$hashname = $time.'-'.$hash.strrchr($_FILES['fielname']['name'], '.');
$status = 'pending';

if(!move_uploaded_file ($_FILES['fielname']['tmp_name'], $path.$hashname  )){
     //failed
     //do somehing for errors.
     die();
}


//store record in db

http://php.net/manual/en/function.move -file-caricato.php

  1. Crea collegamento (varia in base al percorso), il modo più semplice è creare il collegamento in questo modo http://www.example.com/download?file={$hash} ma è più brutto di http://www.example.com/download/{$hash}

  2. il collegamento dei clic dell'utente va alla pagina di download.

ottieni INPUT e cerca il record

$hash = $_GET['file'];

$stmt = $PDO->prepare("SELECT * FROM attachments WHERE hash = :hash LIMIT 1");  
$stmt->execute([":hash" => $hash]);

$row = $stmt->fetch(PDO::FETCH_ASSOC);

print_r($row);

http://php.net/manual/en/intro.pdo.php

ecc....

Saluti!