JavaScript: Wie erzeugt man eine UUID?
Dafür gibt es inzwischen eine eingebaute Funktion, die Antwort ist also eine
Zeile. Lesenswert ist der Teil danach: warum der immer noch kursierende
Math.random()-Einzeiler etwas erzeugt, das identisch aussieht und es nicht
ist.
Die eingebaute Variante
crypto.randomUUID();
crypto.randomUUID() : ffa090d3-7fbd-4bf3-8d97-890afad4d395
crypto.randomUUID()
ist im Browser und in Node Standard, kryptografisch zufällig und braucht keine
Abhängigkeit.
Was die Ziffern bedeuten
fb4dacbb-4555-4bcd-9d4c-68f06907de67
^ ^
the 13th hex digit is always 4 (the version)
the 17th is one of 8, 9, a, b (the variant)
length: 36 groups: 8-4-4-4-12
Zwei der 32 Hex-Ziffern sind nicht zufällig. Die 4 sagt, dass es Version 4
ist — zufallsbasiert, im Gegensatz zur Zeitstempel- und MAC-basierten
Version 1 — und die Variantenziffer sagt, welchem Layout der Rest folgt. Übrig
bleiben 122 Zufallsbits, nicht 128.
Eindeutig genug?
200000 generated, 200000 distinct -> 0 collisions
Dieser Lauf beweist für sich genommen nichts; 200.000 aus 2¹²² wären nie kollidiert. Die Zahl, die die Frage beantwortet, ist die Geburtstagsschranke: Für eine Kollision mit 50 Prozent Wahrscheinlichkeit brauchst du rund 2,7 × 10¹⁸ UUIDs. So viele erzeugst du nicht.
Die Math.random()-Variante
looks fine : 36ff6e83-c8d6-4099-a130-1bcaaf63fd03
Richtige Länge, richtige Gruppen, richtige Versionsziffer. Nichts an der Ausgabe verrät, dass sie anders ist — genau deshalb landet der Einzeiler weiter an Stellen, an denen er nichts zu suchen hat.
Math.random()
ist kein kryptografischer Generator und behauptet das auch nicht. V8
implementiert es mit xorshift128+, pro Kontext geseedet; mit genug Ausgabe lässt
sich der Zustand rekonstruieren und damit auch vergangene und künftige Werte.
Also: in Ordnung für eine DOM-ID oder einen React-Key. Nicht für ein Session-Token, einen Passwort-Reset-Link oder einen API-Schlüssel.
Auch der Wertebereich ist wissenswert:
highest of 1,000,000 draws: 0.9999992620226952 ( always < 1 )
Math.random() liefert [0, 1) — nie 1. Jede Skalierung, die davon ausgeht,
dass das obere Ende erreichbar ist, liegt um einen Fall daneben.
Selbst bauen
Wenn du die Bytes brauchst:
const b = crypto.getRandomValues(new Uint8Array(16));
b[6] = (b[6] & 0x0f) | 0x40; // Version 4
b[8] = (b[8] & 0x3f) | 0x80; // Variante
5d2ae888-b1e6-4ab4-b19c-92a5dfeb5511 version: 4 variant: b
Die beiden maskierten Bytes sind das, was daraus eine gültige v4 macht statt 16 zufälliger Bytes mit Bindestrichen darin.
Wenn eine UUID zu viel ist
16 random bytes as hex : d5e610b00f536b932eea7d8e5aee3018
16 random bytes base64url: bG7V6rQ4pEnAjtb1AcLCPg
Dieselben 128 Bit, weniger Zeichen. Keine UUID, und nicht sortierbar — willst du IDs, die sich nach Erzeugungszeit sortieren, schau dir UUIDv7 an, statt etwas Eigenes zu erfinden.
Warum es manchmal undefined ist
browsers: needs a secure context (https or localhost).
crypto.randomUUID
ist auf einen sicheren Kontext beschränkt. Über einfaches http:// ist es
schlicht nicht vorhanden, und der Fehler, den du bekommst, ist ein TypeError
über den Aufruf von undefined. Das ist fast immer der Grund, warum es “nicht
funktioniert” — kein alter Browser, sondern eine über http ausgelieferte Seite.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).