Vous avez probablement dĂ©jĂ utilisĂ© des UUID dans vos projets et pensiez qu'ils Ă©taient uniques. Jetons un coup d'Ćil aux principaux aspects de l'implĂ©mentation et voyons pourquoi les UUID sont pratiquement uniques, car il existe une infime possibilitĂ© que les mĂȘmes valeurs se produisent.
L'implémentation moderne des UUID remonte à la RFC 4122, qui décrit cinq approches différentes pour générer ces identifiants. Nous allons passer en revue chacun d'eux et parcourir l'implémentation des versions 1 et 4.
Théorie
UUID (IDentifier universellement unique) est un nombre de 128 bits utilisé dans le développement logiciel comme identifiant unique pour les éléments. Sa représentation textuelle classique est une série de 32 caractÚres hexadécimaux, séparés par des tirets en cinq groupes dans le modÚle 8-4-4-4-12.
Par exemple:
3422b448-2460-4fd2-9183-8000de6f8343
Les informations d'implémentation UUID sont intégrées dans cette séquence apparemment aléatoire de caractÚres:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Les valeurs aux positions M et N définissent respectivement la version et la variante de l'UUID.
Version
Le numéro de version est identifié par les quatre bits les plus significatifs à la position M. Il existe aujourd'hui les versions suivantes:
Option
Ce champ définit le modÚle des informations intégrées dans l'UUID. L'interprétation de tous les autres bits de l'UUID dépend de la valeur de la variante.
Nous le déterminons par les 1 à 3 premiers bits les plus significatifs à la position N.
Aujourd'hui, l'option 1 est le plus souvent utilisée, dans laquelle
MSB0
est Ă©gal 1
et MSB1
Ă©gal 0
. Cela signifie que donnée de caractÚres génériques - bits sélectionnés
- seules valeurs possibles sont 8
, 9
, A
ou B
.
MĂ©mo:
1 0 0 0 = 8
1 0 0 1 = 9
1 0 1 0 = A
1 0 1 1 = B
Donc, si vous voyez un UUID avec de telles valeurs Ă la position N, il s'agit de l'identifiant de l'option 1.
Version 1 (heure + identifiant d'hÎte unique ou aléatoire)
Dans ce cas, l'UUID est gĂ©nĂ©rĂ© comme ceci: une propriĂ©tĂ© d'identification du pĂ©riphĂ©rique qui gĂ©nĂšre l'UUID est ajoutĂ©e Ă l'heure actuelle, le plus souvent c'est l'adresse MAC (Ă©galement appelĂ©e ID de nĆud).
L'identifiant est obtenu en concaténant une adresse MAC de 48 bits, un horodatage de 60 bits, une séquence d'horloge «unique» de 14 bits et 6 bits réservés aux UUID de version et de variante.
La séquence d'horloge est simplement une valeur qui est incrémentée chaque fois que l'horloge est modifiée.
L'horodatage utilisé dans cette version est le nombre d'intervalles de 100 nanosecondes depuis le 15 octobre 1582, date à laquelle le calendrier grégorien est né.
Vous connaissez peut-ĂȘtre le systĂšme temporel Unix depuis le dĂ©but d'une Ă©poque. C'est juste un type diffĂ©rent de Day Zero. Il existe des services sur le Web qui peuvent vous aider Ă transformer une reprĂ©sentation temporelle en une autre, alors ne nous attardons pas lĂ -dessus.
Bien que cette implĂ©mentation semble assez simple et fiable, l'utilisation de l'adresse MAC de la machine sur laquelle l'identifiant est gĂ©nĂ©rĂ© ne permet pas de considĂ©rer cette mĂ©thode comme universelle. Surtout lorsque la sĂ©curitĂ© est le critĂšre principal. Par consĂ©quent, dans certaines mises en Ćuvre, au lieu de l'identificateur de nĆud, 6 octets alĂ©atoires pris Ă partir d'un gĂ©nĂ©rateur de nombres alĂ©atoires protĂ©gĂ© par cryptographie sont utilisĂ©s.
La construction de la version 1 de l'UUID ressemble Ă ceci:
- Les 32 bits les moins significatifs de l'horodatage UTC actuel sont pris. Ce seront les 4 premiers octets (8 caractÚres hexadécimaux) de l'UUID [
TimeLow
]. - Les 16 bits du milieu de l'horodatage UTC actuel sont pris. Ce seront les 2 octets suivants (4 caractÚres hexadécimaux) [
TimeMid
]. - Les 2 octets suivants (4 caractÚres hexadécimaux) concaténent les 4 bits de la version UUID avec les 12 MSB restants de l'horodatage UTC actuel (qui a un total de 60 bits) [
TimeHighAndVersion
]. - Les 1-3 bits suivants dĂ©finissent la variante de version d'UUID. Les bits restants contiennent une sĂ©quence d'horloge qui ajoute un peu d'alĂ©atoire Ă cette implĂ©mentation. Cela Ă©vite les collisions lorsque plusieurs gĂ©nĂ©rateurs UUID fonctionnent sur le mĂȘme systĂšme: soit l'horloge systĂšme est reculĂ©e pour le gĂ©nĂ©rateur, soit le changement d'heure est ralenti [
ClockSequenceHiAndRes && ClockSequenceLow
]. - Les 6 derniers octets (12 caractĂšres hexadĂ©cimaux, 48 bits) sont l '"ID de nĆud", qui est gĂ©nĂ©ralement l'adresse MAC du gĂ©nĂ©rateur [
NodeID
].
L'UUID version 1 est généré à l'aide de la concaténation:
TimeLow + TimeMid + TimeHighAndVersion + (ClockSequenceHiAndRes && ClockSequenceLow) + NodeID
Ătant donnĂ© que cette implĂ©mentation dĂ©pend de l'horloge, nous devons gĂ©rer les situations de pointe. PremiĂšrement, pour minimiser la corrĂ©lation entre les systĂšmes, par dĂ©faut, la sĂ©quence d'horloge est considĂ©rĂ©e comme un nombre alĂ©atoire - cela n'est fait qu'une seule fois dans tout le cycle de vie du systĂšme. Cela nous donne l'avantage supplĂ©mentaire de prendre en charge les ID de nĆud qui peuvent ĂȘtre transportĂ©s Ă travers les systĂšmes, car la sĂ©quence d'horloge initiale est complĂštement indĂ©pendante de l'ID de nĆud.
N'oubliez pas que le but principal de l'utilisation d'une sĂ©quence d'horloge est d'ajouter un peu de caractĂšre alĂ©atoire Ă notre Ă©quation. Les bits de sĂ©quence d'horloge aident Ă Ă©tendre l'horodatage et Ă s'adapter aux situations dans lesquelles plusieurs UUID sont gĂ©nĂ©rĂ©s avant mĂȘme que l'horloge du processeur ne change. De cette façon, nous Ă©vitons de crĂ©er les mĂȘmes identifiants lorsque l'horloge est reculĂ©e (l'appareil est Ă©teint) ou que l'identifiant du nĆud change. Si l'horloge a Ă©tĂ© reculĂ©e, ou aurait pu l'ĂȘtre (par exemple, alors que le systĂšme Ă©tait arrĂȘtĂ©) et que le gĂ©nĂ©rateur d'UUID ne peut pas ĂȘtre sĂ»r que les identifiants ont Ă©tĂ© gĂ©nĂ©rĂ©s avec des horodatages plus rĂ©cents que la valeur d'horloge spĂ©cifiĂ©e, alors la sĂ©quence d'horloge doit ĂȘtre modifiĂ©e. Si nous connaissons sa valeur antĂ©rieure, nous pouvons simplement l'augmenter;sinon, il doit ĂȘtre dĂ©fini de maniĂšre alĂ©atoire ou avec un PRNG de haute qualitĂ©.
Version 2 (sécurité d'un environnement informatique distribué)
La principale différence entre cette version et la précédente est qu'au lieu du "caractÚre aléatoire" sous la forme des bits les moins significatifs de la séquence d'horloge, un identifiant caractéristique du systÚme est utilisé ici. Souvent, il ne s'agit que de l'ID de l'utilisateur actuel. La version 2 est moins utilisée, elle diffÚre trÚs peu de la version 1, alors passons à autre chose.
Version 3 (nom + hachage MD5)
Si des identifiants uniques sont nécessaires pour les informations de nom ou de dénomination, l'UUID est généralement la version 3 ou la version 5.
Ils codent toutes les entitĂ©s «nommĂ©es» (sites, DNS, texte brut, etc.) en une valeur UUID. Plus important encore, le mĂȘme UUID sera gĂ©nĂ©rĂ© pour le mĂȘme espace de noms ou texte.
Notez que l'espace de noms lui-mĂȘme est un UUID.
let namespace = âdigitalbunker.devâ
let namespaceUUID = UUID3(.DNS, namespace)
// Ex:
UUID3(namespaceUUID, â/category/things-you-should-know-1/â)
4896c91b-9e61-3129-87b6-8aa299028058
UUID3(namespaceUUID, â/category/things-you-should-know-2/â)
29be0ee3-fe77-331e-a1bf-9494ec18c0ba
UUID3(namespaceUUID, â/category/things-you-should-know-3/â)
33b06619-1ee7-3db5-827d-0dc85df1f759
Dans cette implémentation, l'espace de noms UUID est converti en une chaßne d'octets concaténée avec le nom d'entrée, puis haché avec MD5, ce qui donne 128 bits pour l'UUID. Nous réécrivons ensuite certains des bits pour reproduire fidÚlement la version et les informations de version, et laissons le reste intact.
Il est important de comprendre que ni l'espace de noms ni le nom d'entrĂ©e ne peuvent ĂȘtre calculĂ©s en fonction de l'UUID. C'est une opĂ©ration irrĂ©versible. La seule exception est la force brute lorsque l'une des valeurs (espace de nom ou texte) est dĂ©jĂ connue de l'attaquant.
Avec la mĂȘme entrĂ©e, les UUID gĂ©nĂ©rĂ©s des versions 3 et 5 seront dĂ©terministes.
Version 4 (PRNG)
Implémentation la plus simple.
6 bits sont réservés pour la version et la variante, il reste 122 bits. Cette version génÚre simplement 128 bits aléatoires, puis en remplace 6 par des données de version et de version.
De tels UUID sont totalement dépendants de la qualité du PRNG (générateur de nombres pseudo-aléatoires). Si son algorithme est trop simple ou s'il manque de valeurs initiales, la probabilité de répéter les identificateurs augmente.
Dans les langues modernes, l'UUID version 4 est le plus souvent utilisée.
Son implémentation est assez simple:
- Nous générons 128 bits aléatoires.
- RĂ©Ă©crivez certains bits avec la version et les informations de version correctes:
- Prenez le septiĂšme bit et
0x0F
ET pour effacer le grignotage élevé. Et puis0x40
OR est utilisé pour attribuer la version 4. - Ensuite, nous prenons le neuviÚme octet, effectuons une
0x3F
opération AND sur c et une0x80
opération OR sur celui-ci.
- Prenez le septiĂšme bit et
- Convertissez 128 bits en hexadécimal et insérez des tirets.
Version 5 (nom + SHA-1-hash)
La seule différence par rapport à la version 3 est que nous utilisons l'algorithme de hachage SHA-1 au lieu de MD5. Cette version est préférée à la troisiÚme (SHA-1> MD5).
Entraine toi
L'un des avantages importants des UUID est que leur caractÚre unique ne dépend pas d'une autorité d'autorisation centrale ou d'une coordination entre différents systÚmes. N'importe qui peut créer un UUID avec une certaine certitude que personne d'autre ne générera cette valeur dans un avenir prévisible.
Cela permet de combiner des identifiants créés par différents participants dans une base de données, ou de déplacer des identifiants entre des bases de données avec une probabilité de collision négligeable.
Les UUID peuvent ĂȘtre utilisĂ©s comme clĂ©s primaires dans les bases de donnĂ©es, comme noms uniques pour les fichiers tĂ©lĂ©chargĂ©s, comme noms uniques pour toutes les sources Web. Vous n'avez pas besoin d'une autoritĂ© d'autorisation centrale pour les gĂ©nĂ©rer. Mais c'est une solution Ă double tranchant. En raison de l'absence de contrĂŽleur, il est impossible de suivre les UUID gĂ©nĂ©rĂ©s.
Il y a quelques autres inconvĂ©nients qui doivent ĂȘtre traitĂ©s. Le caractĂšre alĂ©atoire inhĂ©rent augmente la sĂ©curitĂ©, mais rend le dĂ©bogage plus difficile. En outre, l'UUID peut ĂȘtre redondant dans certaines situations. Disons que cela n'a pas de sens d'utiliser 128 bits pour identifier de maniĂšre unique les donnĂ©es dont la taille totale est infĂ©rieure Ă 128 bits.
Unicité
Il peut sembler que si vous disposez de suffisamment de temps, vous pouvez répéter une valeur. Surtout dans le cas de la version 4. Mais en réalité ce n'est pas le cas. Si vous deviez générer un milliard d'UUID par seconde sur 100 ans, la probabilité que l'une des valeurs se répÚte serait d'environ 50%. Ceci étant donné que le PRNG fournit une quantité suffisante d'entropie (vrai hasard), sinon la probabilité d'un double sera plus élevée. Un exemple plus illustratif: si vous avez généré 10 trillions d'UUID, la probabilité d'apparition de deux valeurs identiques est de 0,00000006%.
Et dans le cas de la version 1, l'horloge ne sera remise Ă zĂ©ro que dans 3603. Donc, si vous ne prĂ©voyez pas de maintenir votre service opĂ©rationnel jusqu'en 1583, vous ĂȘtes en sĂ©curitĂ©.
Cependant, la probabilitĂ© d'apparition d'un double demeure, et dans certains systĂšmes, ils essaient d'en tenir compte. Mais dans la grande majoritĂ© des cas, les UUID peuvent ĂȘtre considĂ©rĂ©s comme totalement uniques. Si vous avez besoin de plus de preuves, voici une simple visualisation de la probabilitĂ© de collision en pratique.