Saya menggunakan baris ini untuk menghasilkan id sha1 untuk node.js:
crypto.createHash('sha1').digest('hex');
Masalahnya adalah itu mengembalikan id yang sama setiap saat.
Apakah mungkin membuatnya menghasilkan id acak setiap kali sehingga saya dapat menggunakannya sebagai id dokumen database?
Jawaban:
Lihat di sini: Bagaimana cara menggunakan Crypto node.js untuk membuat hash HMAC-SHA1? Saya akan membuat hash dari stempel waktu saat ini + nomor acak untuk memastikan keunikan hash:
var current_date = (new Date()).valueOf().toString(); var random = Math.random().toString(); crypto.createHash('sha1').update(current_date + random).digest('hex');
sumber
243.583.606.221.817.150.598.111.409x lebih banyak entropi
Saya akan merekomendasikan menggunakan crypto.randomBytes . Tidak
sha1
, tapi untuk tujuan id, ini lebih cepat, dan sama "acaknya".var id = crypto.randomBytes(20).toString('hex'); //=> f26d60305dae929ef8640a75e70dd78ab809cfe9
String yang dihasilkan akan dua kali lebih panjang dari byte acak yang Anda hasilkan; setiap byte yang dikodekan menjadi hex adalah 2 karakter. 20 byte akan menjadi 40 karakter hex.
Menggunakan 20 byte, kami memiliki
256^20
atau 1.461.501.637.330.902.918.203.684.832.716.283.019.655.932.542.976 nilai keluaran unik. Ini identik dengan kemungkinan output SHA1 160-bit (20-byte).Mengetahui hal ini, tidak terlalu berarti bagi kami untuk
shasum
byte acak kami. Ini seperti melempar dadu dua kali tetapi hanya menerima lemparan kedua; Apa pun yang terjadi, Anda memiliki 6 kemungkinan hasil setiap lemparan, jadi lemparan pertama sudah cukup.Mengapa ini lebih baik?
Untuk memahami mengapa ini lebih baik, pertama-tama kita harus memahami cara kerja fungsi hashing. Fungsi hashing (termasuk SHA1) akan selalu menghasilkan keluaran yang sama jika masukan yang sama diberikan.
Katakanlah kita ingin menghasilkan ID tetapi input acak kita dihasilkan oleh lemparan koin. Kami memiliki
"heads"
atau"tails"
Jika
"heads"
muncul lagi, output SHA1 akan sama seperti saat pertama kaliOk, jadi lemparan koin bukanlah generator ID acak yang bagus karena kita hanya memiliki 2 kemungkinan keluaran.
Jika kami menggunakan cetakan 6-sisi standar, kami memiliki 6 kemungkinan input. Tebak berapa banyak kemungkinan keluaran SHA1? 6!
Sangat mudah untuk menipu diri sendiri dengan berpikir hanya karena output dari fungsi kita terlihat sangat acak, bahwa itu adalah sangat acak.
Kami berdua setuju bahwa lemparan koin atau dadu 6 sisi akan menjadi generator id acak yang buruk, karena kemungkinan hasil SHA1 kami (nilai yang kami gunakan untuk ID) sangat sedikit. Tetapi bagaimana jika kita menggunakan sesuatu yang memiliki keluaran lebih banyak? Seperti stempel waktu dengan milidetik? Atau JavaScript
Math.random
? Atau bahkan kombinasi keduanya ?!Mari kita hitung berapa banyak id unik yang akan kita dapatkan ...
Keunikan stempel waktu dengan milidetik
Saat menggunakan
(new Date()).valueOf().toString()
, Anda mendapatkan nomor 13 karakter (misalnya,1375369309741
). Namun, karena ini adalah nomor yang diperbarui secara berurutan (sekali per milidetik), hasilnya hampir selalu sama. Mari lihatfor (var i=0; i<10; i++) { console.log((new Date()).valueOf().toString()); } console.log("OMG so not random"); // 1375369431838 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431839 // 1375369431840 // 1375369431840 // OMG so not random
Agar adil, untuk tujuan perbandingan, dalam satu menit (waktu pelaksanaan operasi yang murah hati), Anda akan memiliki
60*1000
atau60000
unik.Keunikan
Math.random
Sekarang, saat menggunakan
Math.random
, karena cara JavaScript mewakili bilangan titik mengambang 64-bit, Anda akan mendapatkan nomor dengan panjang antara 13 dan 24 karakter. Hasil yang lebih panjang berarti lebih banyak digit yang berarti lebih banyak entropi. Pertama, kita perlu mencari tahu panjang mana yang paling memungkinkan.Skrip di bawah ini akan menentukan panjang mana yang paling memungkinkan. Kami melakukan ini dengan menghasilkan 1 juta nomor acak dan menambah penghitung berdasarkan
.length
jumlah masing-masing nomor.// get distribution var counts = [], rand, len; for (var i=0; i<1000000; i++) { rand = Math.random(); len = String(rand).length; if (counts[len] === undefined) counts[len] = 0; counts[len] += 1; } // calculate % frequency var freq = counts.map(function(n) { return n/1000000 *100 });
Dengan membagi setiap penghitung dengan 1 juta, kita mendapatkan probabilitas panjang angka yang dikembalikan
Math.random
.Jadi, meskipun itu tidak sepenuhnya benar, mari bermurah hati dan katakan Anda mendapatkan keluaran acak sepanjang 19 karakter;
0.1234567890123456789
. Karakter pertama akan selalu0
dan.
, jadi kami hanya mendapatkan 17 karakter acak. Ini menyisakan10^17
+1
(untuk kemungkinan0
; lihat catatan di bawah) atau 100.000.000.000.000.001 unik.Jadi, berapa banyak masukan acak yang dapat kita hasilkan?
Oke, kami menghitung jumlah hasil untuk stempel waktu milidetik dan
Math.random
Itu adalah satu mati 6.000.000.000.000.000.000.060.000 sisi. Atau, untuk membuat angka ini lebih mudah dicerna, ini kira - kira sama dengan angka
Kedengarannya cukup bagus, bukan? Nah, mari kita cari tahu ...
SHA1 menghasilkan nilai 20-byte, dengan kemungkinan hasil 256 ^ 20. Jadi kami benar-benar tidak menggunakan SHA1 secara maksimal. Berapa banyak yang kita gunakan?
Stempel waktu milidetik dan Math.random hanya menggunakan 4,11e-27 persen dari potensi 160-bit SHA1!
Kucing suci, bung! Lihat semua angka nol itu. Jadi seberapa jauh lebih baik itu
crypto.randomBytes(20)
? 243.583.606.221.817.150.598.111.409 kali lebih baik.Catatan tentang
+1
dan frekuensi nolJika Anda bertanya-tanya tentang
+1
, itu mungkin untukMath.random
mengembalikan0
yang berarti ada 1 lagi kemungkinan hasil unik yang harus kami perhitungkan.Berdasarkan pembahasan yang terjadi di bawah ini, saya penasaran dengan frekuensi yang
0
akan muncul. Ini sedikit scriptrandom_zero.js
,, saya buat untuk mendapatkan beberapa data#!/usr/bin/env node var count = 0; while (Math.random() !== 0) count++; console.log(count);
Kemudian, saya menjalankannya dalam 4 utas (saya memiliki prosesor 4-inti), menambahkan output ke file
$ yes | xargs -n 1 -P 4 node random_zero.js >> zeroes.txt
Jadi ternyata a
0
tidak sulit didapat. Setelah 100 nilai dicatat, rata-rata adalahKeren! Diperlukan lebih banyak penelitian untuk mengetahui apakah angka itu setara dengan distribusi seragam
Math.random
implementasi v8sumber
Date
buruk dalam menghasilkan benih yang baik.Math.random
akan pernah menghasilkan0.
crypto.randomBytes
pasti cara untuk pergi ^^Lakukan juga di browser!
Anda dapat melakukan ini di sisi klien di browser modern, jika Anda mau
// str byteToHex(uint8 byte) // converts a single byte to a hex string function byteToHex(byte) { return ('0' + byte.toString(16)).slice(-2); } // str generateId(int len); // len - must be an even number (default: 40) function generateId(len = 40) { var arr = new Uint8Array(len / 2); window.crypto.getRandomValues(arr); return Array.from(arr, byteToHex).join(""); } console.log(generateId()) // "1e6ef8d5c851a3b5c5ad78f96dd086e4a77da800" console.log(generateId(20)) // "d2180620d8f781178840"
Persyaratan browser
sumber
Number.toString(radix)
tidak selalu menjamin nilai 2 digit (misalnya:(5).toString(16)
= "5", bukan "05"). Ini tidak masalah kecuali Anda bergantung pada hasil akhir Anda agar memilikilen
panjang karakter yang tepat . Dalam hal ini Anda dapat menggunakanreturn ('0'+n.toString(16)).slice(-2);
di dalam fungsi peta Anda.id
atribut, pastikan ID dimulai dengan huruf: [A-Za-z].Menggunakan
crypto
adalah pendekatan yang baik karena ini adalah modul asli dan stabil, tetapi ada beberapa kasus di mana Anda dapat menggunakanbcrypt
jika Anda ingin membuat hash yang sangat kuat dan aman. Saya menggunakannya untuk kata sandi, ia memiliki banyak teknik untuk hashing, membuat salt, dan membandingkan kata sandi.const salt = bcrypt.genSaltSync(saltRounds); const hash = bcrypt.hashSync(myPlaintextPassword, salt);
const hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
Untuk contoh lainnya, Anda dapat memeriksa di sini: https://www.npmjs.com/package/bcrypt
sumber