Używam tej linii do generowania identyfikatora sha1 dla node.js:
crypto.createHash('sha1').digest('hex');
Problem w tym, że za każdym razem zwraca ten sam identyfikator.
Czy jest możliwe, aby za każdym razem generował losowy identyfikator, abym mógł go używać jako identyfikatora dokumentu bazy danych?
Odpowiedzi:
Spójrz tutaj: Jak użyć kodu node.js Crypto do utworzenia skrótu HMAC-SHA1? Utworzyłbym hash aktualnego znacznika czasu + losową liczbę, aby zapewnić unikalność skrótu:
źródło
243,583,606,221,817,150,598,111,409x większa entropia
Polecam użycie crypto.randomBytes . Nie jest
sha1
, ale dla celów identyfikacyjnych jest szybszy i równie „losowy”.Wynikowy ciąg będzie dwa razy dłuższy niż losowe bajty, które wygenerujesz; każdy bajt zakodowany w kodzie szesnastkowym to 2 znaki. 20 bajtów to 40 znaków szesnastkowych.
Korzystanie 20 bajtów, mamy
256^20
lub 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976 unikalne wartości wyjściowe. Jest to identyczne z 160-bitowymi (20-bajtowymi) możliwymi wyjściami SHA1.Wiedząc o tym, nie ma to dla nas znaczenia dla
shasum
naszych losowych bajtów. To tak, jakby dwukrotnie rzucić kostką, ale zaakceptować tylko drugi rzut; nie ważne co, masz 6 możliwych wyników w każdym rzucie, więc pierwszy rzut jest wystarczający.Dlaczego tak jest lepiej?
Aby zrozumieć, dlaczego jest to lepsze, musimy najpierw zrozumieć, jak działają funkcje haszujące. Funkcje haszujące (w tym SHA1) zawsze generują te same dane wyjściowe, jeśli podane są te same dane wejściowe.
Powiedzmy, że chcemy wygenerować identyfikatory, ale nasze losowe dane wejściowe są generowane przez rzut monetą. Mamy
"heads"
lub"tails"
Jeśli
"heads"
pojawi się ponownie, wyjście SHA1 będzie takie samo, jak za pierwszym razemOk, więc rzut monetą nie jest świetnym generatorem losowych identyfikatorów, ponieważ mamy tylko 2 możliwe wyjścia.
Jeśli używamy standardowej sześciościennej matrycy, mamy 6 możliwych wejść. Zgadnij, ile możliwych wyjść SHA1? 6!
Łatwo się oszukać, myśląc tylko dlatego, że wynik naszej funkcji wygląda bardzo przypadkowo, że jest bardzo przypadkowy.
Obaj zgadzamy się, że rzut monetą lub sześciościenna kostka stworzyłby zły generator losowych identyfikatorów, ponieważ nasze możliwe wyniki SHA1 (wartość, której używamy jako ID) są bardzo nieliczne. Ale co, jeśli użyjemy czegoś, co ma o wiele więcej wyników? Jak znacznik czasu z milisekundami? Albo JavaScript
Math.random
? A może nawet połączenie tych dwóch ?!Obliczmy, ile unikalnych identyfikatorów otrzymalibyśmy ...
Wyjątkowość sygnatury czasowej z milisekundami
Podczas używania
(new Date()).valueOf().toString()
otrzymujesz 13-znakową liczbę (np1375369309741
.). Jednak ponieważ jest to numer aktualizowany sekwencyjnie (raz na milisekundę), wyniki są prawie zawsze takie same. SpójrzmyAby być uczciwym, dla celów porównawczych, w danej minucie (hojny czas wykonania operacji) będziesz mieć unikaty
60*1000
lub60000
.Wyjątkowość
Math.random
Teraz, gdy używasz
Math.random
, ze względu na sposób, w jaki JavaScript reprezentuje 64-bitowe liczby zmiennoprzecinkowe, otrzymasz liczbę o długości od 13 do 24 znaków. Dłuższy wynik oznacza więcej cyfr, co oznacza większą entropię. Najpierw musimy dowiedzieć się, która długość jest najbardziej prawdopodobna.Poniższy skrypt określi, która długość jest najbardziej prawdopodobna. Robimy to, generując 1 milion liczb losowych i zwiększając licznik na podstawie
.length
każdej liczby.Dzieląc każdy licznik przez 1 milion, otrzymujemy prawdopodobieństwo długości zwróconej liczby
Math.random
.Tak więc, nawet jeśli nie jest to do końca prawdą, bądźmy hojni i powiedzmy, że otrzymujesz losowe wyjście o długości 19 znaków;
0.1234567890123456789
. Pierwsze postacie zawsze będą0
i.
, więc tak naprawdę otrzymujemy tylko 17 losowych postaci. To pozostawia nam10^17
+1
(możliwe0
; zobacz uwagi poniżej) lub 100 000 000 000 000 001 unikatów.Więc ile losowych danych wejściowych możemy wygenerować?
Ok, obliczyliśmy liczbę wyników dla milisekundowego znacznika czasu i
Math.random
To pojedyncza kostka 6,000,000,000,000,000 060,000. Lub, aby uczynić tę liczbę bardziej strawną dla człowieka, jest to mniej więcej taka sama liczba jak
Brzmi całkiem nieźle, prawda? Cóż, dowiedzmy się ...
SHA1 generuje 20-bajtową wartość z możliwymi 256 ^ 20 wynikami. Więc naprawdę nie używamy SHA1 do pełnego potencjału. Ile używamy?
Milisekundowy znacznik czasu, a Math.random wykorzystuje tylko 4,11e-27 procent 160-bitowego potencjału SHA1!
Święte koty, człowieku! Spójrz na te wszystkie zera. Więc o ile lepiej
crypto.randomBytes(20)
? 243,583,606,221,817,150,598,111,409 razy lepszy.Uwagi dotyczące
+1
i częstotliwości zerJeśli zastanawiasz się nad tym
+1
, możliweMath.random
jest zwrócenie a,0
co oznacza, że istnieje jeszcze jeden możliwy unikalny wynik, który musimy uwzględnić.Opierając się na dyskusji, która odbyła się poniżej, byłem ciekawy, jaka
0
będzie częstotliwość a . Oto mały skrypt,random_zero.js
zrobiłem, aby uzyskać daneNastępnie uruchomiłem go w 4 wątkach (mam 4-rdzeniowy procesor), dołączając dane wyjściowe do pliku
Okazuje się więc, że
0
nie jest to takie trudne. Po zarejestrowaniu 100 wartości średnia wyniosłaChłodny! Wymagane byłyby dalsze badania, aby dowiedzieć się, czy ta liczba jest równa jednorodnemu rozkładowi
Math.random
implementacji wersji 8źródło
Date
produkcja dobrych nasion jest okropna.Math.random
kiedykolwiek wyprodukował0.
crypto.randomBytes
to zdecydowanie najlepsza droga ^^Zrób to też w przeglądarce!
Jeśli chcesz, możesz to zrobić po stronie klienta w nowoczesnych przeglądarkach
Wymagania dotyczące przeglądarki
źródło
Number.toString(radix)
nie zawsze gwarantuje wartość dwucyfrową (np.(5).toString(16)
= „5”, a nie „05”). Nie ma to znaczenia, chyba że zależy Ci, aby końcowy wynik miałlen
długość dokładnie znaków. W takim przypadku możesz użyćreturn ('0'+n.toString(16)).slice(-2);
wewnątrz swojej funkcji mapy.id
atrybutu, upewnij się, że identyfikator zaczyna się od litery: [A-Za-z].