UUID有多独特?

Jas*_*son 413 uuid guid uniqueidentifier

使用UUID唯一标识某些东西是安全的(我将它用于上传到服务器的文件)?据我了解,它基于随机数.然而,在我看来,如果有足够的时间,它最终会重复自我,只是纯粹的机会.是否有更好的系统或某种类型的模式来缓解这个问题?

小智 400

非常安全:

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

警告:

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

来源:维基百科关于通用唯一标识符的文章中的重复随机UUID概率部分(链接导致从2016年12月开始修订,然后编辑重新编写该部分).

另请参阅同一个Universe唯一标识符文章Collisions上有关同一主题的当前部分.

  • 我喜欢维基百科的这一部分:然而,这些概率仅在使用足够的熵生成UUID时才成立.否则,重复的概率可能会显着更高,因为统计分散可能更低.那么重复注意这句话的真正机会是什么呢.我们无法在计算机上创建真正的随机数,是吗? (18认同)
  • 实际上,很多工作已经开始寻找方法将尽可能多的熵("真正的随机性",我猜你称之为)引入随机数API.见http://en.wikipedia.org/wiki/Entropy_%28computing%29 (4认同)
  • 这实际上比我想象的更高的碰撞概率.生日悖论,我想. (3认同)
  • @linus_hologram - 这并不是真正的 JS 问题。熵源通常在操作系统级别配置。 (3认同)

rei*_*ein 140

如果"给予足够的时间"意味着100年并且你以每秒10亿的速度创造它们,那么是的,你有50%的机会在100年后发生碰撞.

  • 但只有在为这些ID使用256艾字节的存储空间之后. (174认同)
  • 有趣的是,你可以连续产生两个相同的,当然是令人难以置信的巧合,运气和神圣干预,但尽管有不可思议的几率,它仍然是可能的!:D是的,它不会发生.只是说当你创造一个复制品时,想到那个时刻的乐趣!截图视频! (16认同)
  • @TheTahaan那不是随机的意思.它并不意味着"完全不可预测" - 通常它们遵循某种分布.如果你翻转10个硬币,获得2个头,接着是3个尾巴,接着是5个头的几率非常低(2 ^ -10,大约0.001).它确实是随机的,但我们绝对_can_知道获得特定结果的_机会.我们不能提前说是否会发生. (14认同)
  • 为了解释这个实现错误的原因,他们使用的是版本1 UUID,它依赖于时间戳和mac地址的组合来实现其唯一性.但是,如果您足够快地生成UUID,则时间戳也不会增加.在这种情况下,您的UUID生成算法应该跟踪上次使用的时间戳并将其递增1.它们显然未能采取该步骤.但是,同一台机器在短时间内正确生成的所有版本1 UUID都会表现出明显的相似之处,但应该仍然是唯一的. (5认同)
  • 独特性纯粹是因为随机性吗?还是有其他因素?(例如时间戳,IP等) (4认同)
  • 如果某些东西取决于随机数不相同,则那些随机数不是随机数.如果它们真的是随机的,那么就无法知道获得重复的可能性. (2认同)
  • @MemetOlsen 没有重新检查我的数字,但人们经常高估。我相信我确实使用了生日悖论公式来得出这个数字。 (2认同)

Hoy*_*len 97

有多种类型的UUID,因此"安全性"取决于您使用的类型(UUID规范称之为"版本").

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

  • 版本4是随机数UUID.有六个固定位,UUID的其余部分是122位随机性.请参阅维基百科或其他分析,描述重复的可能性.

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

  • 版本2类似于版本1,但具有较小的时钟,因此它将更快地环绕.但由于版本2 UUID适用于DCE,因此您不应使用这些.

因此,对于所有实际问题,他们是安全的 如果你对将其留在概率中感到不舒服(例如,你担心地球在你的一生中被大型小行星摧毁的那种类型),那么请确保使用版本1 UUID并保证它是唯一的(在你的一生中,除非你计划住在公元3603年).

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

  • 一个人不需要在一秒内生成1000万个版本1 UUID以遇到重复; 一个必须只在一个"tick"的范围内生成一批16,384个UUID,以便溢出序列号.我已经看到这种情况发生在一个实现上,天真地依赖于(1)具有μs级粒度的时钟源,并且(2)不保证是单调的(系统时钟不是).小心你使用的UUID生成代码,并且特别警惕**基于时间的UUID生成器.他们很难做对,所以**在使用它们之前要对它们进行加载测试**. (4认同)
  • *理论上*,它是由制造商唯一分配的。 (2认同)

Mat*_*att 17

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

许多UUID生成器使用版本4随机数.然而,其中许多使用伪随机数生成器来生成它们.

如果使用播种时间很短的PRNG来生成UUID,我会说它根本不安全.

因此,它仅与用于生成它的算法一样安全.

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

在我的情况下,我正在使用的PRNG是一个mersenne twister,我小心它的种子来自多个来源,包括/ dev/urandom.Mersenne twister的周期为2 ^ 19937 - 1.在我看到重复的uuid之前,这将是一段很长的时间.


Dav*_*ogt 14

引自维基百科:

因此,任何人都可以创建一个UUID并使用它来识别某些东西,并确信该标识符永远不会被任何人无意中用于其他任何事情.

它继续详细解释它实际上是多么安全.所以回答你的问题:是的,它足够安全.


Par*_*ppa 8

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

使用UUID的重点在于您相信它可以更好地提供比您自己能够做到的唯一ID.这与使用第三方加密库而不是自己编写密码库背后的原理相同.自己动手可能会更有趣,但这样做通常不那么负责任.


los*_*ost 6

对于 UUID4,我使得 ID 的数量大约与边长 360,000 公里的立方体形状的盒子中的沙粒一样多。这是一个边长约为木星直径 2 1/2 倍的盒子。

工作以便有人可以告诉我我是否搞砸了单位:

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

  • 这实际上非常有帮助,让我意识到这可能没问题,还有其他事情需要担心。喵喵 (11认同)
  • 实际上我猜这是假设 100% 打包的情况,所以也许我应该为此添加一个因素! (3认同)
  • 显然它是一个完整的盒子,否则我可以说“一个已知宇宙大小的盒子,充满了 0.0000000002%”(例如,未计算!),这是缺乏描述性的。我认为包装因子对于上述计算来说是一个更大的问题,但至少它是保守的(即比 100% 更现实的值会使盒子更大)。 (2认同)

小智 5

多年来一直这样做.永远不会遇到问题.

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

它唯一的缺点是,当您编写一些查询以快速查找某些信息时,您正在进行大量复制和粘贴密钥.你不再容易记住那些短暂的ID.


Tsc*_*cka 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);
   }
}
};
};
Run Code Online (Sandbox Code Playgroud)
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/>
Run Code Online (Sandbox Code Playgroud)


Ste*_*n C 5

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

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

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

以下是几种方法:

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

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


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

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

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

  • 我只是想指出,方法 2 基本上违背了使用 UUID 的主要目的,此时您不妨只使用经典的编号 ID。 (4认同)
  • 我不同意。连续编号 ID 的缺陷是它们太容易被猜到。您应该能够以一种使 UUID 难以猜测的方式实现方法 2。 (2认同)
  • 嗯,是的,也不是。这取决于要求 id 唯一的上下文。如果它们只需要在封闭系统中唯一,那么使用短随机字符串并将它们全部存储在数据库(或其他东西)中以检查重复项是可行的。但这并不能保证*通用*独特性。如果在系统的生命周期内生成的唯一 ID 的数量足够大,您将遇到扩展问题,假设唯一 ID 需要随着时间的推移而保持唯一......而不仅仅是在某个时间点。 (2认同)