Saya pikir saya akan menulis singkat (bagi saya ini singkat) "jawaban" supaya saya bisa meringkas poin saya.
Beberapa "Praktik Terbaik" saat membuat sistem penyimpanan file. Penyimpanan file adalah kategori yang luas sehingga jarak tempuh Anda mungkin berbeda untuk beberapa di antaranya. Anggap saja sebagai saran tentang apa yang menurut saya berfungsi dengan baik.
Nama file Jangan menyimpan file dengan nama yang diberikan oleh pengguna akhir. Mereka dapat dan akan menggunakan semua jenis karakter jelek yang akan membuat hidup Anda sengsara. Beberapa bisa seburuk '
tanda kutip tunggal, yang di linux pada dasarnya membuatnya jadi tidak mungkin untuk membaca, atau bahkan menghapus file ( langsung ). Beberapa hal bisa tampak sederhana seperti spasi tetapi tergantung di mana Anda menggunakannya dan OS di server Anda, Anda dapat berakhir dengan
one%20two.txt
atau one+two.txt
atau one two.txt
yang mungkin atau mungkin tidak menimbulkan semua jenis masalah di tautan Anda.
Hal terbaik yang harus dilakukan adalah membuat hash, seperti sha1
ini bisa sesederhana {user_id}{orgianl_name}
Nama pengguna membuatnya lebih kecil kemungkinannya untuk bertabrakan dengan nama file pengguna lain.
Saya lebih suka melakukan file_hash('sha1', $contents)
dengan begitu jika seseorang mengunggah file yang sama lebih dari sekali, Anda dapat menangkapnya (isinya sama hashnya sama). Tetapi jika Anda berharap memiliki file besar, Anda mungkin ingin melakukan penandaan bangku untuk melihat jenis kinerja yang dimilikinya. Saya kebanyakan menangani file kecil sehingga berfungsi dengan baik untuk itu.-perhatikan- bahwa dengan stempel waktu file masih dapat disimpan karena nama lengkapnya berbeda, tetapi membuatnya cukup mudah dilihat, dan dapat diverifikasi di database.
Terlepas dari apa yang Anda lakukan, saya akan mengawalinya dengan stempel waktu time().'-'.$filename
. Ini adalah informasi yang berguna untuk dimiliki, karena ini adalah waktu mutlak file itu dibuat.
Adapun nama pengguna memberikan file. Simpan saja dalam catatan database. Dengan cara ini Anda dapat menunjukkan kepada mereka nama yang mereka harapkan, tetapi gunakan nama yang Anda tahu selalu aman untuk tautan.
$filename ='beberapa jelek^ 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";
Keluaran
Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg
Anda dapat mencobanya di sini
Jalur jangan pernah menyimpan path lengkap ke file. Yang Anda butuhkan dalam database adalah hash dari pembuatan nama hash. Jalur "root" ke folder tempat file disimpan harus dilakukan dalam PHP. Ini memiliki beberapa manfaat.
- mencegah pemindahan direktori. Karena Anda tidak melewati bagian jalan mana pun di sekitar Anda, Anda tidak perlu terlalu khawatir tentang seseorang yang tergelincir
\..\..
di sana dan pergi ke tempat-tempat yang tidak seharusnya. Contoh buruknya adalah seseorang menimpa.htpassword
file dengan mengunggah file bernama itu dengan direktori melintang di dalamnya. - Memiliki tautan yang tampak lebih seragam, ukuran seragam, kumpulan karakter yang seragam.
https://en.wikipedia.org/wiki/Directory_traversal_attack
- Pemeliharaan. Jalur berubah, Server berubah. Tuntutan pada sistem Anda berubah. Jika Anda perlu memindahkan file-file itu, tetapi Anda menyimpan path lengkap absolut ke mereka di DB, Anda terjebak menempelkan semuanya bersama-sama dengan
symlinks
atau memperbarui semua catatan Anda.
Ada beberapa pengecualian untuk ini. Jika Anda ingin menyimpannya di folder bulanan atau berdasarkan nama pengguna. Anda dapat menyimpan bagian jalur itu, di bidang terpisah. Tetapi bahkan dalam kasus itu, Anda dapat membangunnya secara dinamis berdasarkan data yang disimpan dalam catatan. Saya telah menemukan yang terbaik untuk menyimpan info jalur sesedikit mungkin. Dan mereka membuat konfigurasi atau konstanta yang dapat Anda gunakan di semua tempat yang Anda perlukan untuk meletakkan jalur ke file.
Juga path
dan link
sangat berbeda, jadi dengan menyimpan hanya nama Anda dapat menautkannya dari halaman PHP apa pun yang Anda inginkan tanpa harus mengurangi data dari jalurnya. Saya selalu merasa lebih mudah untuk menambahkan nama file daripada mengurangi dari jalur.
Basis Data (hanya beberapa saran, penggunaan dapat bervariasi )Seperti biasa dengan data tanyakan pada diri sendiri, siapa, apa, di mana, kapan
- id -
int
peningkatan otomatis kunci utama - id_pengguna -
int
kunci asing, siapa mengunggahnya - hash -
char[40] *sha1*, unique
apa hashnya - nama hash -
varchar
{timestampl}-{hash}.{ext} di mana nama file di hard drive - nama file -
varchar
nama asli yang diberikan oleh pengguna, dengan begitu kita dapat menunjukkan kepada mereka nama yang mereka harapkan ( jika itu penting ) - status -
enum[public,private,deleted,pending.. etc]
status file, tergantung pada kasus penggunaan Anda, Anda mungkin harus meninjau file, atau mungkin sebagian bersifat pribadi hanya pengguna yang dapat melihatnya, mungkin sebagian bersifat publik, dll. - status_date -
timestamp|datetime
kali statusnya berubah. - tanggal_buat -
timestamp|datetime
kapan saat file dibuat, stempel waktu lebih disukai karena membuat beberapa hal lebih mudah tetapi harus menggunakan stempel waktu yang sama dalam nama hash, dalam hal ini. - ketik -
varchar
- mime type, bisa berguna untuk setting mime type saat download dll.
Jika Anda mengharapkan pengguna yang berbeda untuk mengunggah file yang sama dan Anda menggunakan file_hash
anda dapat membuat hash
bidang indeks unik gabungan dari user_id
dan hash
cara ini hanya akan bentrok jika pengguna yang sama mengunggah file yang sama. Anda juga dapat melakukannya berdasarkan stempel waktu dan hash, tergantung pada kebutuhan Anda.
Itu hal dasar yang bisa saya pikirkan, ini tidak mutlak hanya beberapa bidang yang saya pikir akan berguna.
Sangat berguna untuk memiliki hash sendiri, jika Anda menyimpannya sendiri, Anda dapat menyimpannya di CHAR(40)
untuk sha1 (memakan lebih sedikit ruang di DB daripada VARCHAR
) dan atur susunannya, ke UTF8_bin
yang biner. Hal ini membuat pencarian di atasnya peka huruf besar/kecil. Meskipun kemungkinan tabrakan hash kecil, ini hanya menambahkan sedikit perlindungan karena hash adalah huruf besar dan kecil.
Anda selalu dapat membuat hashname
dengan cepat jika Anda menyimpan ekstensi, dan stempel waktu terpisah. Jika Anda mendapati diri Anda membuat sesuatu berulang kali, Anda mungkin hanya ingin menyimpannya di DB untuk menyederhanakan pekerjaan di PHP.
Saya suka hanya menempatkan hash di tautan, tidak ada ekstensi apa pun sehingga tautan saya terlihat seperti ini.
http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea
Sangat sederhana, generik nyata, aman di url selalu ukuran yang sama dll.
hashname
untuk "file" ini akan menjadi seperti ini
1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg
Jika Anda memiliki konflik dengan file yang sama dan pengguna yang berbeda (yang saya sebutkan di atas). Anda selalu dapat menambahkan bagian stempel waktu ke dalam tautan, user_id atau keduanya. Jika Anda menggunakan user_id, mungkin berguna untuk meninggalkannya dengan nol. Misalnya beberapa pengguna mungkin memiliki ID:1
dan beberapa mungkin ID:234
jadi Anda bisa meninggalkannya di 4 tempat dan menjadikannya 0001
dan 0234
. Kemudian tambahkan itu ke hash, yang hampir tidak terlihat:
1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg
Yang penting di sini adalah karena sha1
selalu 40
dan id selalu 4
kita dapat memisahkan keduanya secara akurat dan mudah. Dan dengan cara ini, Anda masih bisa mencarinya secara unik. Ada banyak pilihan yang berbeda tetapi banyak tergantung pada kebutuhan Anda.
Akses Seperti mengunduh. Anda harus selalu menampilkan file dengan PHP, jangan beri mereka akses langsung ke file. Cara terbaik adalah menyimpan file di luar webroot ( di atas public_html
, atau www
folder). Kemudian di PHP Anda dapat mengatur header ke jenis yang benar dan pada dasarnya membaca file. Ini berfungsi untuk hampir semua hal kecuali video. Saya tidak menangani video jadi itu topik di luar pengalaman saya. Tapi menurut saya lebih baik untuk memikirkannya karena semua data file adalah teks, header-nya yang membuat teks itu menjadi gambar, atau file excel atau pdf.
Keuntungan besar dari tidak memberi mereka akses langsung ke file adalah jika Anda memiliki situs keanggotaan, tidak ingin konten Anda dapat diakses tanpa login, Anda dapat dengan mudah memeriksa PHP jika mereka login sebelum memberi mereka konten. Dan, karena file berada di luar webroot, mereka tidak dapat mengaksesnya dengan cara lain.
Yang paling penting adalah memilih sesuatu yang konsisten, yang masih cukup fleksibel untuk menangani semua kebutuhan Anda.
Saya yakin saya dapat membuat lebih banyak lagi, tetapi jika Anda memiliki saran, jangan ragu untuk berkomentar.
ALUR PROSES DASAR
- Pengguna mengirimkan formulir (
enctype="multipart/form-data"
)
https://www.w3schools.com/tags/att_form_enctype.asp
- Server menerima kiriman dari formulir, Super Globals
$_POST
dan$_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)
]
];
-
Periksa kesalahan
if(!$_FILES['fielname']['error'])
-
Bersihkan nama tampilan
$filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");
-
Simpan file, buat catatan DB ( PSUDO-CODE )
Seperti ini:
$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 -upload-file.php
-
Buat tautan (bervariasi berdasarkan perutean), cara sederhana adalah dengan membuat tautan Anda seperti ini
http://www.example.com/download?file={$hash}
tapi lebih jelek darihttp://www.example.com/download/{$hash}
-
tautan klik pengguna menuju ke halaman unduhan.
dapatkan INPUT dan cari catatan
$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
dll....
Semangat!