web-dev-qa-db-fra.com

A quel point UUID est-il unique?

Est-il sûr d'utiliser UUID pour identifier de manière unique quelque chose (je l'utilise pour les fichiers téléchargés sur le serveur)? Si je comprends bien, il est basé sur des nombres aléatoires. Cependant, il me semble que si on lui donnait assez de temps, il finirait par se répéter, par pur hasard. Existe-t-il un meilleur système ou un modèle quelconque pour atténuer ce problème?

383
Jason

Très sécuritaire:

le risque annuel qu'une météorite frappe une personne donnée est estimé à une chance sur 17 milliards, ce qui signifie que la probabilité est d'environ 0,00000000006 (6 × 10−11), ce qui équivaut à la probabilité de créer quelques dizaines de milliards de dollars d’UUID par an et d’avoir un doublon. En d’autres termes, ce n’est qu’après avoir généré 1 milliard d’UUID par seconde pour les 100 prochaines années que la probabilité de créer un seul duplicata serait d’environ 50%.

Caveat:

Cependant, ces probabilités ne sont valables que lorsque les UUID sont générés avec une entropie suffisante. Sinon, la probabilité de doublons pourrait être considérablement plus élevée, car la dispersion statistique pourrait être plus faible. Lorsque des identifiants uniques sont requis pour les applications distribuées, afin que les identificateurs UUID ne se contredisent pas même lorsque des données provenant de nombreux périphériques sont fusionnées, le caractère aléatoire des valeurs initiales et des générateurs utilisés sur chaque périphérique doit être fiable pendant toute la durée de vie de l'application. Lorsque cela n'est pas réalisable, la RFC4122 recommande d'utiliser une variante d'espace de noms.

Source: The Probabilité aléatoire de doublons de la section UUID de l'article de Wikipedia sur les identificateurs universellement uniques (le lien mène à une révision à partir de décembre 2016 avant la modification de la section modifiée) ).

Voir également la section en cours sur le même sujet et le même article d'identificateur unique universel, Collisions .

376
lutz

Si vous entendez 100 ans par "suffisamment de temps" et que vous les créez à un taux d'un milliard par seconde, alors oui, vous avez 50% de chances d'avoir une collision après 100 ans.

136
rein

Il existe plusieurs types d’UUID. Par conséquent, le degré de sécurité dépend du type (que les spécifications UUID appellent la "version") que vous utilisez.

  • La version 1 correspond à l'UUID basé sur l'heure et l'adresse MAC. Le 128 bits contient 48 bits pour l'adresse MAC de la carte réseau (qui est attribuée de manière unique par le fabricant) et une horloge de 60 bits avec une résolution de 100 nanosecondes. Cette horloge enveloppe 3603 A.D. donc ces UUID sont sécurisés au moins jusqu’alors (sauf si vous avez besoin de plus de 10 millions de nouveaux UUID par seconde ou que quelqu'un clone votre carte réseau). Je dis "au moins" parce que l'horloge commence le 15 octobre 1582, vous avez donc environ 400 ans après que l'horloge se soit écoulée avant qu'il ne reste même une petite possibilité de duplication.

  • La version 4 est le numéro aléatoire UUID. Il y a six bits fixes et le reste de l'UUID est de 122 bits aléatoire. Voir Wikipedia ou une autre analyse décrivant à quel point un doublon est peu probable.

  • La version 3 utilise MD5 et la version 5 utilise SHA-1 pour créer ces 122 bits au lieu d'un générateur de nombres aléatoires ou pseudo-aléatoires. Donc, en termes de sécurité, la version 4 est considérée comme un problème statistique (tant que vous vous assurez que le traitement de l'algorithme digest est toujours unique).

  • La version 2 est similaire à la version 1, mais avec une horloge plus petite, elle sera donc bouclée beaucoup plus tôt. Mais puisque les UUID de la version 2 sont destinés à DCE, vous ne devriez pas les utiliser.

Donc, pour tous les problèmes pratiques, ils sont en sécurité. Si vous ne vous sentez pas à l'aise avec les probabilités (par exemple, vous êtes le type de personne qui craint que la Terre ne soit détruite par un astéroïde de grande taille dans votre vie), assurez-vous d'utiliser un UUID de version 1 et il est garanti qu'il est unique ( dans votre vie, sauf si vous prévoyez de vivre après 3603).

Alors, pourquoi tout le monde n'utilise-t-il pas simplement les UUID de la version 1? Cela est dû au fait que les UUID de la version 1 révèlent l'adresse MAC de la machine sur laquelle il a été généré et qu'ils peuvent être prévisibles, ce qui peut avoir des conséquences sur la sécurité de l'application utilisant ces UUID.

96
Hoylen

La réponse à cette question peut dépendre en grande partie de la version de l’UUID.

De nombreux générateurs d'UUID utilisent un nombre aléatoire de version 4. Cependant, beaucoup d'entre eux utilisent Pseudo un générateur de nombres aléatoires pour les générer.

Si un PRNG mal ensemencé avec une courte période est utilisé pour générer l'UUID, je dirais que ce n'est pas très sûr du tout.

Par conséquent, il est aussi sûr que les algorithmes utilisés pour le générer.

D'un autre côté, si vous connaissez la réponse à ces questions, je pense qu'une version 4 d'uuid devrait être très sûre à utiliser. En fait, je l'utilise pour identifier des blocs sur un système de fichiers réseau et, jusqu'à présent, je n'ai pas eu de conflit.

Dans mon cas, le PRNG que j'utilise est un twister de mersenne et je suis prudent avec la façon dont il est semé, qui provient de plusieurs sources, y compris/dev/urandom. Le twister de Mersenne a une période de 2 ^ 19937 - 1. Il faudra beaucoup de temps avant que je voie une répétition de l'uuid.

17
Matt

Citant de Wikipedia :

Ainsi, n'importe qui peut créer un UUID et l'utiliser pour identifier quelque chose avec une assurance raisonnable que l'identifiant ne sera jamais utilisé involontairement par quiconque pour autre chose.

Il explique ensuite en détail à quel point il est sécurisé. Donc, pour répondre à votre question: oui, c'est assez sûr.

14
Dave Vogt

Les schémas UUID utilisent généralement non seulement un élément pseudo-aléatoire, mais également l'heure système actuelle et un identifiant matériel souvent unique, le cas échéant, tel qu'une adresse MAC de réseau.

L'utilisation de l'UUID a pour objectif essentiel de vous permettre de fournir un identifiant unique plus performant que vous ne pourriez le faire vous-même. C’est la même raison pour utiliser une bibliothèque de cryptographie tierce plutôt que de lancer la vôtre. Le faire soi-même peut être plus amusant, mais c'est généralement moins responsable de le faire.

8
Parappa

Voici un extrait de test pour vous permettre de tester son caractère unique. inspiré par le commentaire de @ scalabl3

Ce qui est drôle, c’est que vous pourriez générer deux rangées identiques, bien sûr, à des niveaux époustouflants de coïncidences, de chance et d’interventions divines. Pourtant, malgré des obstacles insondables, c’est toujours possible! : D Oui, ça n'arrivera pas. je dis juste pour l’amusement de penser à ce moment où vous avez créé un duplicata! Capture d'écran vidéo! - scalabl3 20 oct. 15 à 19:11

Si vous vous sentez chanceux, cochez la case, il ne vérifie que les identifiants générés. Si vous souhaitez une vérification de l'historique, laissez la case décochée. S'il vous plaît noter que vous pourriez manquer de bélier à un moment donné si vous le laissez non vérifié. J'ai essayé de le rendre convivial pour le processeur afin que vous puissiez abandonner rapidement en cas de besoin, appuyez simplement de nouveau sur le bouton Run Snippet ou quittez la page.

Math.log2 = Math.log2 || function(n){ return Math.log(n) / Math.log(2); }
  Math.trueRandom = (function() {
  var crypt = window.crypto || window.msCrypto;

  if (crypt && crypt.getRandomValues) {
      // if we have a crypto library, use it
      var random = function(min, max) {
          var rval = 0;
          var range = max - min;
          if (range < 2) {
              return min;
          }

          var bits_needed = Math.ceil(Math.log2(range));
          if (bits_needed > 53) {
            throw new Exception("We cannot generate numbers larger than 53 bits.");
          }
          var bytes_needed = Math.ceil(bits_needed / 8);
          var mask = Math.pow(2, bits_needed) - 1;
          // 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111

          // Create byte array and fill with N random numbers
          var byteArray = new Uint8Array(bytes_needed);
          crypt.getRandomValues(byteArray);

          var p = (bytes_needed - 1) * 8;
          for(var i = 0; i < bytes_needed; i++ ) {
              rval += byteArray[i] * Math.pow(2, p);
              p -= 8;
          }

          // Use & to apply the mask and reduce the number of recursive lookups
          rval = rval & mask;

          if (rval >= range) {
              // Integer out of acceptable range
              return random(min, max);
          }
          // Return an integer that falls within the range
          return min + rval;
      }
      return function() {
          var r = random(0, 1000000000) / 1000000000;
          return r;
      };
  } else {
      // From http://baagoe.com/en/RandomMusings/javascript/
      // Johannes Baagøe <[email protected]>, 2010
      function Mash() {
          var n = 0xefc8249d;

          var mash = function(data) {
              data = data.toString();
              for (var i = 0; i < data.length; i++) {
                  n += data.charCodeAt(i);
                  var h = 0.02519603282416938 * n;
                  n = h >>> 0;
                  h -= n;
                  h *= n;
                  n = h >>> 0;
                  h -= n;
                  n += h * 0x100000000; // 2^32
              }
              return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
          };

          mash.version = 'Mash 0.9';
          return mash;
      }

      // From http://baagoe.com/en/RandomMusings/javascript/
      function Alea() {
          return (function(args) {
              // Johannes Baagøe <[email protected]>, 2010
              var s0 = 0;
              var s1 = 0;
              var s2 = 0;
              var c = 1;

              if (args.length == 0) {
                  args = [+new Date()];
              }
              var mash = Mash();
              s0 = mash(' ');
              s1 = mash(' ');
              s2 = mash(' ');

              for (var i = 0; i < args.length; i++) {
                  s0 -= mash(args[i]);
                  if (s0 < 0) {
                      s0 += 1;
                  }
                  s1 -= mash(args[i]);
                  if (s1 < 0) {
                      s1 += 1;
                  }
                  s2 -= mash(args[i]);
                  if (s2 < 0) {
                      s2 += 1;
                  }
              }
              mash = null;

              var random = function() {
                  var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
                  s0 = s1;
                  s1 = s2;
                  return s2 = t - (c = t | 0);
              };
              random.uint32 = function() {
                  return random() * 0x100000000; // 2^32
              };
              random.fract53 = function() {
                  return random() +
                      (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
              };
              random.version = 'Alea 0.9';
              random.args = args;
              return random;

          }(Array.prototype.slice.call(arguments)));
      };
      return Alea();
  }
}());

Math.guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)    {
      var r = Math.trueRandom() * 16 | 0,
          v = c == 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
  });
};
function logit(item1, item2) {
    console.log("Do "+item1+" and "+item2+" equal? "+(item1 == item2 ? "OMG! take a screenshot and you'll be epic on the world of cryptography, buy a lottery ticket now!":"No they do not. shame. no fame")+ ", runs: "+window.numberofRuns);
}
numberofRuns = 0;
function test() {
   window.numberofRuns++;
   var x = Math.guid();
   var y = Math.guid();
   var test = x == y || historyTest(x,y);

   logit(x,y);
   return test;

}
historyArr = [];
historyCount = 0;
function historyTest(item1, item2) {
    if(window.luckyDog) {
       return false;
    }
    for(var i = historyCount; i > -1; i--) {
        logit(item1,window.historyArr[i]);
        if(item1 == history[i]) {
            
            return true;
        }
        logit(item2,window.historyArr[i]);
        if(item2 == history[i]) {
            
            return true;
        }

    }
    window.historyArr.Push(item1);
    window.historyArr.Push(item2);
    window.historyCount+=2;
    return false;
}
luckyDog = false;
document.body.onload = function() {
document.getElementById('runit').onclick  = function() {
window.luckyDog = document.getElementById('lucky').checked;
var val = document.getElementById('input').value
if(val.trim() == '0') {
    var intervaltimer = window.setInterval(function() {
         var test = window.test();
         if(test) {
            window.clearInterval(intervaltimer);
         }
    },0);
}
else {
   var num = parseInt(val);
   if(num > 0) {
        var intervaltimer = window.setInterval(function() {
         var test = window.test();
         num--;
         if(num < 0 || test) {
    
         window.clearInterval(intervaltimer);
         }
    },0);
   }
}
};
};
Please input how often the calulation should run. set to 0 for forever. Check the checkbox if you feel lucky.<BR/>
<input type="text" value="0" id="input"><input type="checkbox" id="lucky"><button id="runit">Run</button><BR/>
5
Tschallacka

Je le fais depuis des années. Ne jamais rencontrer un problème.

J'ai l'habitude de configurer mes bases de données pour avoir une seule table contenant toutes les clés et les dates modifiées, etc. Vous n'avez jamais rencontré de problème de duplication de clés.

Le seul inconvénient est que lorsque vous écrivez des requêtes pour trouver rapidement des informations, vous faites beaucoup de copier-coller des clés. Vous n'avez plus le mot de passe facile à mémoriser.

5
Posthuma

Je suis d'accord avec les autres réponses. Les UUID sont suffisamment sûrs pour presque toutes les applications pratiques1et certainement pour le vôtre.

Mais supposons (hypothétiquement) qu'ils ne le sont pas.

Existe-t-il un meilleur système ou un modèle quelconque pour atténuer ce problème?

Voici quelques approches:

  1. Utilisez un UUID plus grand. Par exemple, au lieu de 128 bits aléatoires, utilisez 256 ou 512 ou ... Chaque bit ajouté à un UUID de type 4 réduira de moitié la probabilité de collision, en supposant que vous disposiez d'une source d'entropie fiable.2.

  2. Construisez un service centralisé ou distribué qui génère des UUID et enregistre tous ceux qu’il a déjà émis. Chaque fois qu'il en génère un nouveau, il vérifie que l'UUID n'a jamais été émis auparavant. Un tel service serait techniquement simple à mettre en œuvre (je pense) si nous supposions que les personnes qui gèrent le service étaient absolument dignes de confiance, incorruptibles, etc. Malheureusement, ils ne le sont pas ... surtout lorsqu'il est possible que des gouvernements interviennent. Donc, cette approche est probablement peu pratique, et peut être3 impossible dans le monde réel.


1 - Si le caractère unique des UUID déterminait si des missiles nucléaires avaient été lancés dans la capitale de votre pays, nombre de vos concitoyens ne seraient pas convaincus par "la probabilité est extrêmement faible". D'où ma "presque tout" qualification.

2 - Et voici une question philosophique pour vous. Est-ce que quelque chose est vraiment vraiment aléatoire? Comment saurions-nous si ce n'était pas le cas? L'univers tel que nous le connaissons est-il une simulation? Y a-t-il un Dieu qui pourrait "ajuster" les lois de la physique pour modifier un résultat?

3 - Si quelqu'un a connaissance de documents de recherche sur ce problème, veuillez commenter.

4
Stephen C

Je ne sais pas si cela vous concerne, mais gardez à l'esprit que les GUID sont globalement uniques, mais les sous-chaînes de GUID ne le sont pas .

2
Grant Wagner