UUID有多独特?


449

使用UUID唯一标识某事物有多安全(我正在将它用于上传到服务器的文件)?据我了解,它是基于随机数的。但是,在我看来,如果有足够的时间,它最终会以纯偶然的机会自我重复。是否有更好的系统或某种模式来缓解此问题?


11
对于足够大的“足够的时间” :)

91
“ UUID有多独特?” 我相信,这是世界上独一无二的。;)
Miles

29
除非您打算在Venus上进行开发,否则GUID应该足够。
skaffman

1
更多详细信息和生成器:在线uuid生成器
Dave

2
“独特”意味着永不碰撞。如果它有可能发生碰撞,那么它并不是唯一的。因此,根据定义,UUID不是唯一的,并且仅在您准备好可能发生的碰撞而不考虑碰撞机会的情况下才是安全的。否则,您的程序就是不正确的。您可以说UUID为“几乎唯一”,但这并不意味着它是“唯一的”。
Eonil '19

Answers:


443

非常安全:

估计某人被陨石击中的年度风险是170亿的一次机会,这意味着该概率约为0.00000000006(6×10 -11),这相当于制造数十万亿个UUID的几率一年内重复一次。换句话说,只有在接下来的100年中每秒生成10亿个UUID之后,才创建一个副本的可能性约为50%。

警告:

但是,仅当使用足够的熵生成UUID时,这些概率才成立。否则,重复的可能性可能会更高,因为统计差异可能会更低。在分布式应用程序需要唯一标识符的情况下,即使合并了许多设备的数据,UUID也不冲突,每个设备上使用的种子和生成器的随机性在应用程序的生命周期内必须可靠。如果这不可行,则RFC4122建议改用命名空间变体。

资料来源:Wikipedia文章“通用唯一标识符”的“ 随机UUID重复概率”部分(链接导致自2016年12月起进行修订,然后对该部分进行了修改)。

另请参阅同一通用唯一标识符文章Collisions中有关同一主题的当前部分。


22
我喜欢维基百科的这一部分:但是,仅当使用足够的熵生成UUID时,这些概率才成立。否则,重复的可能性可能会更高,因为统计差异可能会更低。那么重复注意到这句话的真正机会是什么。我们不能在计算机上创建真实的随机数,可以吗?
勒芒2014年

6
实际上,已经进行了很多工作,找到了将尽可能多的熵(“真正的随机性”,我想您会称呼它)引入随机数API的方法。参见en.wikipedia.org/wiki/Entropy_%28computing%29
broofa 2014年

4
实际上,发生碰撞的可能性比我想象的要高。我猜是生日悖论。
卡梅伦

您可以确认在执行应用程序之间使用UUID是安全的吗?(例如python脚本)
George Sp

很好的例子...:D
NuttLoose

151

如果“给定的时间”是指100年,并且以每秒10亿的速度创建它们,那么是的,在100年后发生碰撞的机率是50%。


185
但是只有在这些ID用完256 EB的存储空间之后。
鲍勃·阿曼

16
有趣的是,您可以连续生成两个相同的东西,当然这在巧合的巧合,运气和神圣干预的水平上来说,尽管有不可思议的赔率,但仍然有可能!:D是的,不会发生。只是出于娱乐考虑创建副本的那一刻而已!截图视频!
scalabl3 2015年

4
唯一性纯粹是因为随机性吗?还是还有其他因素?(例如,时间戳记,ip等)
曾伟石

15
@TheTahaan那不是随机的意思。这并不意味着“完全不可预测”-通常它们遵循某种分布。如果掷出10个硬币,获得2个正面,3个反面和5个正面的机会非常低(2 ^ -10,约0.001)。这确实是随机的,但我们绝对可以知道获得特定结果的机会。我们只是无法提前说是否发生。
理查德·拉斯特

5
只是为了解释此实现的错误之处,他们使用的是版本1 UUID,该版本依赖于时间戳和mac地址的组合来实现其唯一性。但是,如果您生成UUID的速度足够快,则时间戳尚未增加。在这种情况下,您的UUID生成算法应该跟踪上次使用的时间戳并将其递增1。他们显然没有采取该步骤。但是,同一台计算机在短时间内正确生成的所有版本1 UUID都将表现出明显的相似性,但仍应是唯一的。
鲍勃·阿曼

103

UUID的类型不止一种,因此“安全性”取决于您所使用的类型(UUID规范称为“版本”)。

  • 版本1是基于时间的,加上MAC地址UUID。128位包含网卡的MAC地址的48位(由制造商唯一分配)和60位时钟,分辨率为100纳秒。该时钟包裹在3603 AD中,因此至少在此之前这些UUID是安全的(除非您每秒需要超过一千万个新的UUID或有人克隆您的网卡)。我说“至少”是因为时钟始于1582年10月15日,所以在绕完时钟大约400年之后,甚至有很小的重复机会。

  • 版本4是随机数UUID。有六个固定位,UUID的其余部分为122位随机性。请参阅Wikipedia或其他分析,描述重复的可能性很小。

  • 版本3使用MD5,版本5使用SHA-1创建那些122位,而不是随机或伪随机数生成器。因此,就安全性而言,就像版本4是一个统计问题一样(只要您确保摘要算法正在处理的内容始终是唯一的)。

  • 版本2与版本1类似,但时钟更小,因此可以更快地环绕。但是由于版本2的UUID是DCE的,所以您不应该使用它们。

因此,对于所有实际问题,它们都是安全的。如果您对将其留给概率不满意(例如,您是那种担心地球在您的一生中被大型小行星摧毁的人),只需确保您使用的是版本1 UUID,并保证它是唯一的( ,除非您打算活到3603 AD以上)。

那么,为什么每个人都不只是使用版本1 UUID?这是因为版本1 UUID揭示了生成它的机器的MAC地址,并且它们是可以预测的-两件事可能会对使用这些UUID的应用程序带来安全隐患。


1
当同一服务器为许多人生成UUID时,默认为版本1 UUID具有严重问题。我默认使用版本4 UUID,因为您可以快速编写一些东西以任何语言或平台(包括javascript)生成一个。
Justin Bozonier

1
@Hoylen好解释!但这需要夸大吗?
Dinoop paloli 2014年

1
从理论上讲,它是由制造商唯一分配的。
OrangeDog

4
一个人不必在一秒钟内生成1000万个版本1 UUID即可遇到重复;一个人只需在单个“刻度”的范围内生成一批16,384个UUID,即可使序列号溢出。我已经看到这种情况的发生是由于幼稚地依赖于时钟源,该时钟源(1)具有μs级粒度,并且(2)不能保证是单调的(不是系统时钟)。请小心使用您的UUID生成代码,尤其要注意基于时间的UUID生成器。它们很难正确安装,因此在使用它们之前要对其进行负载测试
Mike Strobel'3

生成的v4 UUID之间的时间跨度是否可能导致更多的碰撞概率?我的意思是,在交通繁忙的应用中,假设同时生成数千个uuid,与在相对较长的时间内生成相同数量的uuid相比,发生碰撞的机会是否更多?
乔纳森

18

答案可能很大程度上取决于UUID版本。

许多UUID生成器使用版本4随机数。但是,其中许多使用伪随机数生成器来生成它们。

如果使用种子期较短的PRNG来生成UUID,我会说它根本不是很安全。

因此,它仅与生成它的算法一样安全。

另一方面,如果您知道这些问题的答案,那么我认为第4版uuid应该非常安全。实际上,我正在使用它来识别网络块文件系统上的块,并且到目前为止还没有发生冲突。

就我而言,我使用的PRNG是一台mersenne扭曲器,并且我对它的播种方式非常谨慎,该播种来自多个来源,包括/ dev / urandom。梅森捻线器的周期为2 ^ 19937 −1。这将是非常长的时间,我才能看到重复的uuid。


14

引用维基百科

因此,任何人都可以创建UUID并以合理的信心使用它来标识某物,从而使该标识符永远不会被任何人无意间用于其他任何用途

然后继续详细解释其实际安全性。因此,请回答您的问题:是的,它足够安全。


9

我同意其他答案。UUID对于几乎所有实际用途1都是安全的,当然对您而言也是如此。

但假设(假设)事实并非如此。

是否有更好的系统或某种模式来缓解此问题?

以下是几种方法:

  1. 使用更大的UUID。例如,假设您有可靠的熵源2,则可以使用256或512或...来代替128个随机位,而将每个位添加到4型样式UUID会将碰撞的可能性降低一半。。

  2. 构建一个集中式或分布式服务,该服务生成UUID并记录它曾经发布的每一个。每次生成新的UUID时,都会检查该UUID从未发行过。如果我们假设运行该服务的人员绝对值得信赖,廉洁等,那么从技术上讲,要实现这种服务将是直截了当的。不幸的是,它们并非……尤其是在政府的安全组织有可能干涉的情况下。因此,这种方法可能不切实际,在现实世界中可能是3种不可能的方法。


1-如果UUID的唯一性决定了核导弹是否是在贵国首都发射的,那么许多同胞将不会被“可能性极低”说服。因此,我的“几乎所有”资格。

2-这是您要解决的一个哲学问题。有没有真正随机的东西?我们怎么知道不是吗?我们所知道的宇宙是模拟吗?有没有一位上帝可以想象“调整”物理定律以改变结果?

3-如果有人知道有关此问题的任何研究论文,请发表评论。


我只想指出,方法2基本上违反了使用UUID的主要目的,并且您最好在此时也使用经典的编号ID。
Petr Vnenk

我不同意。顺序编号的ID的缺陷在于它们太容易猜测。您应该能够以一种使UUID难以猜测的方式来实现方法2。
Stephen C

8

UUID方案通常不仅使用伪随机元素,而且还使用当前系统时间,并且使用某种经常唯一的硬件ID(如果有),例如网络MAC地址。

使用UUID的全部意义在于,您相信UUID可以比您自己能够做的更好,可以提供唯一的ID。这与使用第三方加密库而不是自己动手使用相同的理由。自己动手可能会更有趣,但是这样做的责任通常较小。


5

已经做了多年了。永远不会遇到问题。

我通常将数据库设置为具有一个包含所有键和修改后的日期等的表。从来没有遇到过重复密钥的问题。

它的唯一缺点是,当您编写一些查询以快速找到一些信息时,您将进行很多键的复制和粘贴。您再也没有容易记住的简短ID。


5

这是一个测试代码段,供您测试其独特性。受@ scalabl3的评论启发

有趣的是,您可以连续生成两个相同的东西,当然这在巧合的巧合,运气和神圣干预的水平上来说,尽管有不可思议的赔率,但仍然有可能!:D是的,不会发生。只是出于娱乐考虑创建副本的那一刻而已!截图视频!– scalabl3 2015年10月20日,19:11

如果您感到幸运,请选中该复选框,它仅检查当前生成的ID。如果您希望历史记录检查,请不要选中它。请注意,如果不检查它,可能会在某些时候用完ram。我试图使其对cpu友好,因此您可以在需要时快速中止操作,只需再次按下运行代码段按钮或离开页面即可。

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 <baagoe@baagoe.com>, 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 <baagoe@baagoe.com>, 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/>


尝试使用RFC 4122版本1(日期时间和MAC地址)UUID。
zaph

过去一直很快,直到最近更新了chrome。我在4周前注意到了同样的情况。
Tschallacka


3

对于UUID4,我可以使它的ID数量与边长360,000 km的立方体形盒子中的沙粒数量一样多。那是一个盒子,其边长是木星直径的约2 1/2倍。

工作,以便有人可以告诉我是否弄乱了单位:

  • 砂0.00947毫米的晶粒的体积^ 3(
  • UUID4具有122个随机位-> 5.3e36可能的值( Wikipedia
  • 那么多沙粒的体积= 5.0191e34 mm ^ 3或5.0191e + 25m ^ 3
  • 体积= 3.69E8m或369,000km的立方箱的边长
  • 木星直径:139,820km(谷歌)

实际上,我想这是假设包装为100%,所以也许我应该为此添加一个因素!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.