3 Punkte von GN⁺ 2024-08-27 | 1 Kommentare | Auf WhatsApp teilen
  • Es gibt 8 UUID-Versionen von v1 bis v8; eine höhere Zahl bedeutet nicht, dass sie neuer oder besser ist, sondern dass in RFC 9562 unterschiedliche Erzeugungsverfahren definiert sind
  • In der Praxis läuft die Auswahl meist auf v4 und v7 hinaus: v4 ist der Standard für zufällige IDs, v7 eignet sich, wenn eine Sortierung nach Erstellungszeit erforderlich ist
  • v1 und v6 verwenden dieselben Bestandteile, aber bei v6 ist die Feldreihenfolge so geändert, dass eine Sortierung der Erstellungszeit entspricht
  • v3 und v5 erzeugen UUIDs durch Hashing der Eingabedaten; v3 verwendet MD5, v5 SHA-1, und mögliche Eingabewerte sind unter anderem DNS und URL
  • Wenn möglich, sollte man statt v1·v6 v7 verwenden; wenn eine eingabedatenbasierte UUID benötigt wird, ist v5 sinnvoll, und für vollständig benutzerdefinierte UUIDs kommt v8 in Betracht

Erzeugungsverfahren nach UUID-Version

  • UUID-Versionen gibt es von v1 bis v8, und sie sind alle in RFC 9562 definiert
  • Zeitbasierte UUIDs

    • UUID Version 1 wird aus Zeitstempel, monotonem Zähler und MAC-Adresse erzeugt
    • UUID Version 6 verwendet dieselben Daten wie v1, ändert aber die Reihenfolge so, dass beim Sortieren die Erstellungszeit erhalten bleibt
    • UUID Version 7 wird aus Zeitstempel und Zufallsdaten erzeugt
  • Zufällige und benutzerdefinierte UUIDs

    • UUID Version 4 wird vollständig aus Zufallsdaten erzeugt und entspricht am ehesten dem, was viele sich unter einer UUID vorstellen
    • UUID Version 8 ist vollständig benutzerdefiniert, abgesehen von den version/variant-Feldern, die für alle Versionen erforderlich sind
  • UUIDs auf Basis eines Eingabedaten-Hashs

    • UUID Version 3 wird aus dem MD5-Hash der vom Benutzer bereitgestellten Daten erzeugt
      • In der RFC sind DNS und URL als mögliche Eingabewerte genannt
    • UUID Version 5 wird aus dem SHA-1-Hash der vom Benutzer bereitgestellten Daten erzeugt
      • Wie bei v3 kommen auch hier DNS und URL als Eingabekandidaten infrage
  • Reservierte UUIDs

    • UUID Version 2 ist für Sicherheits-IDs reserviert; bekannte Details dazu gibt es nicht

Praktische Auswahlkriterien

  • In den meisten Fällen fällt die Wahl auf v4 oder v7
  • Wenn einfach nur eine zufällige ID benötigt wird, ist v4 die naheliegende Standardwahl
  • Wenn die ID sortierbar sein soll, kann v7 sinnvoll sein
    • Zum Beispiel kann v7 ein Kandidat sein, wenn UUIDs als Datenbankschlüssel verwendet werden
  • v5 oder v8 sind eher für Fälle gedacht, in denen eigene Daten in die UUID eingebettet werden sollen
    • In solchen Fällen wissen Nutzer meist bereits, dass sie genau diesen Typ benötigen
  • Laut RFC verbessert v7 v1 und v6, daher sollte man nach Möglichkeit v7 statt v1·v6 verwenden
    • Wenn v1 oder v6 zwingend erforderlich sind, kann v6 verwendet werden
  • v2 ist für einen nicht näher spezifizierten Sicherheitszweck reserviert
  • v3 wurde durch v5 mit stärkerem Hash ersetzt; auch wenn v3 benötigt wird, wissen Nutzer das oft bereits

1 Kommentare

 
GN⁺ 2024-08-27
Meinungen auf Hacker News
  • Dass es keine Details zu UUID v2 gebe, gilt nur, wenn man ausschließlich den notorisch unklaren RFC liest: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#t...
    Man wird ihr fast nie begegnen, aber es gibt auch eine Version-0-UUID. Sie ist erwähnenswert, weil von ihr die reservierten Bits stammen, durch die später andere „Versionen“ kompatibel definiert werden konnten. Die zugehörige Recherche habe ich in meiner UUID-Bibliothek zusammengetragen: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
    Weil ich sie cool fand, habe ich beschlossen, sie zu unterstützen; allerdings muss ich noch festlegen, wie Datums-Rollover und die noch ältere Apollo-UID-Behandlung funktionieren sollen.

    • Fairerweise zitiert RFC9562 zwei Dokumente zur UUID-Version-2-Spezifikation. RFC4122 war mir allerdings zu kryptisch
      Ich beginne gerade mit der Arbeit an einem informativen RFC zu den historischen UUID-Typen, die im Variant-Raum 0–7 definiert sind, damit Leute sie besser verstehen. Wer mitdiskutieren oder Formulierungen prüfen möchte, kann hier schauen: https://github.com/yocto/draft-yocto-uuid
  • Die Details ließen sich in zwei Minuten finden. Im Artikel dem Link zum DCE-Definitionsabschnitt von RFC 9562 folgen, über den ersten Link in diesem Absatz zur Spezifikation gehen, nach „UUID“ suchen und zu Anhang A springen — dort steht alles
    Der Name ist verwirrend „Universal Unique Identifier“, aber die benötigten Inhalte sind vollständig vorhanden. Es wäre gut, wenn man wenigstens die Links im eigenen Artikel selbst anklickt

    • Daraus kann man zwei Dinge lernen. Erstens, die v2-UUID-Spezifikation zu lesen, und zweitens, aus einer UUID Informationen über die Umstände ihrer Erzeugung abzuleiten
      Im Kontext wird beschrieben, dass andere Versionen aus bestimmten Elementen erzeugt werden, daher bezieht sich der Satz eindeutig auf Letzteres. Der Satz ist zwar etwas unklar, aber schwerlich irreführend
    • Ich bin exakt denselben Weg gegangen und hatte den Eindruck, dass sich der Artikel kaum um Details kümmert, also habe ich ihn nicht bis zum Ende gelesen
      Der Anhang las sich trotzdem interessant, fast wie eine Momentaufnahme von damals
    • Vielleicht wurde der Artikel von einem Sprachmodell geschrieben
  • Ein Standard für kurze UUIDs wie 73WakrfVbNJBaAmhQtEeDv oder bK7nP9xM wäre schön
    Streng genommen wären es wohl keine UUIDs, weil sie irgendwo kollidieren könnten, aber ich hätte gern eine standardisierte Kombination für zufällige IDs, die kurz genug sind, um sie sich zu merken

    • Am nächsten kommt mir ULID in den Sinn. 26 Zeichen in base32, also kurz, 128 Bit, und lexikografisch sortierbar
      Ich denke, es gibt keinen populäreren Standard, weil man immer auf etwas verzichten muss. 128 Bit bedeuten für fast alle Zwecke ein geringes Kollisionsrisiko, aber je kleiner man wird, desto stärker muss man die konkrete Situation und die Folgen von Kollisionen berücksichtigen, was eine Standardisierung erschwert. Andere Encodings wie base64 oder base85 wären kürzer, opfern aber Dinge wie Groß-/Kleinschreibung oder URL-Sicherheit: https://github.com/ulid/spec
    • Wenn man eine kompaktere UUID braucht, kann man eine bestehende UUID als URL-sicheres base64 in einer 22-Zeichen-Form darstellen
      Das ist nur eine andere Darstellung derselben UUID und reversibel. Eine UUID ist letztlich ein 128-Bit-Wert; es ist also eher eine alternative Notation als eine echte Umwandlung
    • Sqids könnten passen. Sie erzeugen deutlich kürzere IDs als UUIDs, sind aber nicht global eindeutig und werden aus Integer-Sequenzen generiert: https://sqids.org/
    • Üblicherweise erzeugt man N Bit Zufall und encodiert sie mit base58. N kann man nach Bedarf wählen
      Damit verliert man den Vorteil monotoner Sortierung mancher UUID-Versionen, aber base58 ist URL-sicher und enthält keine Sonderzeichen. Die Werte lassen sich weiterhin binär speichern. In Postgres könnte man zum Beispiel statt einer Textspalte bytea verwenden
    • Es läuft eine Standardisierung von alternativen Encoding-Verfahren, mit denen sich 128-Bit-UUIDs in kürzerer Textform darstellen lassen
      Die Diskussion ist hier zu finden: https://github.com/uuid6/new-uuid-encoding-techniques-ietf-d...
  • Der Zeitstempel von UUID v7 war für Databend eine große Veränderung. Er wird genutzt, um Metadatendateien in AWS S3 schnell anhand des Zeitstempels zu finden, wodurch Operationen wie vacuum deutlich schneller werden
    PR: https://github.com/datafuselabs/databend/pull/16049

    • Interessant, aber UUIDs zu verwenden, um nach Zeit zu suchen, käme mir nicht sofort in den Sinn. Ich würde wohl ein separates Zeitstempelfeld anlegen
      Der große Vorteil zeitlich sortierbarer UUIDs liegt in der besseren Lokalität. Beim Einfügen neuer Einträge in einen Index landen sie meist am Ende, was günstiger sein kann als zufällige Inserts. Allerdings kann dadurch auch mehr Contention entstehen; daher kann sich ein Hybrid lohnen, bei dem einige Zufallsbits vor den Zeitstempel gesetzt werden, um sortierte „Shards“ zu bilden. Auch Lesezugriffe konzentrieren sich oft auf aktuelle Daten, daher ist es nützlich, wenn aktuelle Daten an einem Ort liegen und gut im Cache landen
    • Auch für Key-Value-Stores wie DynamoDB ist das sehr gut. Es ist viel sauberer als zusammengesetzte Keys mit Zeitstempel oder ISO-Datum als Präfix und besser, als einen sekundären Index für Zeitstempel zu verschwenden
  • Der Zweck von uuid2 ist schwer zu verstehen. Ich wusste nicht einmal, dass es noch mehr solcher Typen gibt, und habe uuid2 zum ersten Mal gesehen, als ich Xandr um die Löschung meiner personenbezogenen Daten bat: https://news.ycombinator.com/item?id=40913915
    Selbst nach dem Lesen von Wikipedia verstehe ich nicht wirklich, warum man einen „universell eindeutigen Identifikator“ schafft und dann mehrere Typen hat, von denen einige ursprünglich sogar so gemacht waren, dass sie bis zum PC zurückverfolgbar sind. Ich frage mich, ob das Beimischen eines Teils des MAC-Codes uuid2 zufälliger macht oder ob es einen anderen Grund gibt. Aus Datenschutzsicht frage ich mich auch, ob man nicht einfach lange Identifikatoren mit sehr vielen möglichen Zeichen verwenden kann, um die Wahrscheinlichkeit von Duplikaten praktisch auszuschließen.

    • Der ursprüngliche Zweck war, Nachrichten in der Apollo Distributed Computing Architecture zu identifizieren. UIDs und später UUIDs waren eine umkehrbare Methode, den Schnittpunkt zweier Dimensionen zu markieren.
      Zwei beliebige Maschinen konnten für dieselben zwei Eingaben dieselbe UID/UUID erzeugen, und der Empfänger der identifizierten Nachricht konnte den Identifikator wieder in seine ursprünglichen Bestandteile zurückführen. Da sie als Labels für flüchtige Nachrichten entworfen wurden, waren die zwei Dimensionen Zeit und Hardware-ID; zunächst wurden Apollo-Seriennummern verwendet, später unter anderem Ethernet-Hardwareadressen.
      Ein großer Teil der Verwirrung kommt meiner Ansicht nach daher, dass Apollo-Ingenieure in der frühen AEGIS-Implementierung begannen, „canned“, also statische und wohlbekannte UIDs, zur Dateisystem-Identifikation zu verwenden. Mit der Zeit verschob sich der übliche Einsatz von UUIDs vollständig: von flüchtigen Identifikatoren, bei denen Kollisionen beabsichtigt waren, hin zu canned Identifikatoren, bei denen Kollisionen vermieden werden müssen; und die zwei Dimensionen wurden zu Zufall und nochmals Zufall.
      Die Geschichte ist noch komplizierter. Microsoft stellte eine der Schlüsselfiguren von Apollo ein, um MSRPC für Windows NT zu entwickeln, und so entstand auch GUID. GUIDs haben eine andere Feldanordnung als UUIDs und sind, anders als viele Quellen behaupten, nicht Mixed-Endian. Microsoft verwendet canned GUIDs gern nicht nur zur Identifikation flüchtiger RPC-Nachrichten, sondern für COM-Klassen, Medien-Codecs und fast alles andere, was wohlbekannte Identifikatoren braucht. Beispiel: https://gix.github.io/media-types/
      Entschuldigung, dass ich im selben Kommentarbereich zweimal auf mein Repository verlinke, aber im README meiner UUID-Bibliothek habe ich begonnen, diese Geschichte aufzuschreiben, und sollte daran weiterarbeiten. Apollo begann 1980, und der Leach/Salz-Entwurf für den UUID-RFC erschien erst 1998; in modernen Standards fehlt also enorm viel: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
  • UUID v4 ist lediglich ein Generator für Zufallsbytes, der Bindestriche an festgelegten Positionen einfügt. Man muss sie nicht unbedingt verwenden; wenn man selbst Zufallsbytes erzeugt, kann man Platz sparen.
    Auch unnötige Bindestriche und Versionsinformationen entfallen.

    • Eine UUID ist eine 128-Bit-Zahl, und die String-Darstellung mit Bindestrichen ist nur eine von mehreren Möglichkeiten, diese Zahl darzustellen.
      Ähnlich wie eine IPv4-Adresse eine 32-Bit-Zahl ist und die „vier durch Punkte getrennten Blöcke“ nur eine ihrer Darstellungen sind. Wenn man UUIDs als String-Format betrachtet, hat man schon das grundlegendste Konzept von UUIDs falsch verstanden. Selbst wenn man nur einen zufälligen Identifikator möchte, finde ich es gut, dass eine zufällige UUID ein kleines Flag-Bit enthält, das sagt: „Das ist als zufällig gedacht.“ Das ist nützlich, wenn man nur einen kontextlosen Identifikator findet.
    • UUID v4 setzt außerdem 4 Bit auf feste Werte, um anzuzeigen, dass es Version 4 ist.
      Ob es nützlich ist, zwischen verschiedenen Erzeugungsmethoden unterschiedliche Namensräume zu schaffen, kann man diskutieren, aber ein gewöhnlicher Zufallsgenerator erzeugt nur mit einer Wahrscheinlichkeit von 1/16 eine gültige UUIDv4. Wenn man natürlich selbst einen UUID-Generator schreiben möchte, ist es trivial, die Bits korrekt zu setzen.
    • Stimmt, aber für die meisten Entwickler liegt der Reiz darin, dass die Implementierung einfach ist. In fast jeder Sprache gibt es eine UUID-Bibliothek, die in einer Zeile funktioniert.
      In Go reicht uuid.New().String(), während es mehr Zeilen und Aufwand braucht, um mit crypto/rand Zufallsdaten zu lesen und in Base64 oder Hex umzuwandeln.
  • Von MAC-basierten Versionen würde ich abraten. Theoretisch kann das alles außer v4 und v7 betreffen, aber v1 ist am schlimmsten.
    Bei v3 gibt es außerdem das Problem, dass MD5 stark gebrochen ist.

    • MD5 ist als kryptografische Hashfunktion „gebrochen“. Als nicht-kryptografische Hashfunktion ist es weiterhin völlig in Ordnung.
  • Die Details außer Nummer 4 kannte ich nicht, aber wirklich nützlich wäre wohl eine Methode mit SHA256-Daten und einem Zähler. Ähnlich wie PBKDF2.
    Das könnte ein abgeleiteter Identifikator sein, der personenbezogene Daten schützt, und man könnte lose nachweisen, dass eine bestimmte UUID aus einem bestimmten Seed abgeleitet wurde.

    • Wenn man das wirklich braucht, kann man UUIDv4 als Encoding-Format betrachten, mit einem kryptografischen Algorithmus 122 Bit Ausgabe erzeugen und diese dann als UUID encodieren.
      Ansonsten wird man wahrscheinlich eine längere Ausgabe haben wollen.
    • Ähnlich wie v3, aber in einer Form, bei der der Hash-Algorithmus konfigurierbar ist.
  • Man kann einfach v7 verwenden.
    Jetzt sind die Sicherheitsexperten dran zu sagen, dass man das nicht sollte.

    • Wenn das Erstellungsdatum eine sensible Information sein kann oder die UUID vollständig unvorhersehbar sein muss, sollte man v4 verwenden. Ansonsten v7.
    • Was mich an UUID und ULID immer gestört hat, ist, dass es meines Wissens keine gute Möglichkeit gibt, sie deterministisch zu erzeugen.
      Für viele Anwendungsfälle wäre es sehr nützlich, beim erneuten Verarbeiten von Daten dieselbe ID erzeugen zu können, aber ich kenne keinen standardisierten Weg, das zu erreichen.
    • Ich verstehe nicht, warum Sicherheitsfragen so leichtfertig abgetan werden.