UUID有多独特?

使用UUID唯一标识某些内容(我正在使用它来标识上传到服务器的文件)有多安全?据我所知,它是基于随机数。然而,在我看来,只要有足够的时间,它最终会完全偶然地重复它自己。是否有更好的系统或某种类型的模式来缓解这个问题?

290007 次浏览

很安全:

一个人被陨石击中的年风险是 估计是170亿分之一的几率,也就是说 概率约为0.00000000006 (6 × 10−11),相当于几率 在一年内创造出几十万亿uuid,并拥有一个uuid 复制。换句话说,只有在每次生成10亿个uuid之后 第二,在接下来的100年里,只创造一个的概率 重复大约是50%

警告:

但是,这些概率仅在生成uuid时成立 使用足够的熵。否则,重复的概率 可能会更高,因为统计上的离散度可能会更高 更低。分布式需要唯一标识符的地方 这样即使来自多个应用程序的数据也不会发生uuid冲突 设备是合并的,随机的种子和生成器使用上 每个设备都必须在应用的生命周期内是可靠的。在哪里 这是不可行的,RFC4122建议使用命名空间变体 相反。< / p >

来源:维基百科关于通用唯一标识符的文章重复的随机UUID概率 section(链接指向2016年12月的修订版,之后编辑重新编辑了该部分)。

还可以参阅同一篇通用唯一标识符文章中关于同一主题的当前部分< em > < / em >的碰撞

引用维基百科:

因此,任何人都可以创建UUID并使用 用它来表示某物 合理相信 标识符永远不会是 无意中被某人用于 其他< / p >

它还非常详细地解释了它的安全性。所以回答你的问题:是的,它足够安全。

如果你所说的“有足够的时间”是指100年,你以每秒10亿的速度创造它们,那么是的,100年后你有50%的几率发生碰撞。

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

使用UUID的关键在于,您相信它能够比您自己更好地提供唯一的ID。这与使用第三方加密库而不是自己开发加密库的原理相同。自己做可能更有趣,但这样做通常不那么负责任。

我已经做了很多年了。永远不要遇到问题。

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

它的唯一缺点是,当您编写一些查询来快速查找一些信息时,您需要进行大量的复制和粘贴键。你不再有简单易记的id了。

我不知道这对你是否重要,但请记住guid是全局唯一的,但guid的子字符串不是

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

  • 版本1是基于时间加上MAC地址UUID。128位包含48位的网卡MAC地址(由制造商唯一分配)和一个分辨率为100纳秒的60位时钟。该时钟在公元3603年结束,因此这些uuid至少在那时是安全的(除非您需要每秒超过1000万个新的uuid或有人克隆您的网卡)。我说“至少”是因为时钟从1582年10月15日开始计时,所以在时钟结束后大约有400年的时间,哪怕只有一点点复制的可能性。

  • 版本4是随机数UUID。UUID有6个固定位,其余的UUID是122位随机的。参见Wikipedia或其他描述副本不太可能的分析。

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

  • 版本2与版本1相似,但使用了更小的时钟,因此它将更快地完成。但是由于版本2的uuid是用于DCE的,所以不应该使用这些uuid。

所以对于所有实际问题,它们都是安全的。如果你不喜欢把它留给概率(例如,你是那种担心地球在你的一生中被一颗大小行星摧毁的人),只要确保你使用版本1的UUID,并且它保证是唯一的(在你的一生中,除非你计划活到公元3603年以后)。

那么,为什么不是每个人都使用版本1的uuid呢?这是因为版本1的uuid揭示了生成它的机器的MAC地址,并且它们是可以预测的——这两件事可能会对使用这些uuid的应用程序产生安全影响。

这个问题的答案很大程度上取决于UUID版本。

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

如果使用一个短周期的低种子PRNG来生成UUID,我认为这一点都不安全。一些随机数生成器的方差也很差。也就是说,更倾向于某些数字。这不会有好结果的。

因此,它的安全性取决于生成它的算法。

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

在我的情况下,我使用的PRNG是一个梅森龙卷风,我很小心,它的播种方式是来自多个来源,包括/dev/ urrandom。梅森龙卷风的周期为2^19937−1。在我看到一个重复的uuid之前,会有很长很长的时间。

因此,选择一个好的库或自己生成它,并确保使用合适的PRNG算法。

这里有一个测试片段供您测试它的独特性。 灵感来自@scalabl3的注释

有趣的是,你可以连续生成2个完全相同的结果,当然是在令人难以置信的巧合、运气和神的干预下,尽管有不可思议的几率,但这仍然是可能的!:是的,不会发生的。我这么说只是为了好玩,想想你创造了一个复制品的那一刻!视频截图!- scalab13 10月20日15日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/>

我同意其他的答案。uuid对于几乎所有的实际目的都是足够安全的,1,当然对你来说也是如此。

但假设(假设)它们不是。

是否有更好的系统或某种类型的模式来缓解这个问题?

这里有一些方法:

  1. 使用更大的UUID。例如,使用256或512或……而不是128随机位。假设您有一个可靠的熵源2,那么您添加到type-4风格UUID中的每一位都会将碰撞的概率降低一半。

  2. 构建一个集中式或分布式服务,生成uuid并记录它曾经发出的每个uuid。每次生成一个新UUID时,它都会检查该UUID以前是否从未发出过。如果我们假设运行服务的人是绝对值得信任的、不可腐败的等等,那么这样的服务在技术上是可以直接实现的(我认为)。不幸的是,他们不是……尤其是当政府安全机构有可能进行干预时。因此,这种方法可能是不切实际的,在现实世界中3.可能是不可能的。


1 -如果uuid的唯一性决定了核导弹是否在你国家的首都发射,你的许多同胞不会相信“这种可能性极低”。因此我的“几乎所有”;资格。< br > 2 -我有一个哲学问题要问你。有什么是真正随机的吗?如果不是,我们怎么知道?我们所知的宇宙是模拟的吗?是否有一个上帝可以想象地“微调”;用物理定律来改变结果?< br > 3 -如果有人知道任何关于这个问题的研究论文,请评论

对于UUID4,我认为在一个边长360000公里的立方体盒子中,id的数量大约与沙粒的数量相同。这是一个边长约为木星直径2.5倍的盒子。

如果我搞砸了单位,就会有人告诉我:

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

我应该提一下,我在亚马逊上买了两个外接希捷驱动器,它们有相同的设备UUID,但PARTUUID不同。大概他们用来格式化硬盘的克隆软件也复制了UUID。

显然,UUID冲突更可能是由于有缺陷的克隆或复制过程而不是由于随机巧合而发生。在计算UUID风险时请记住这一点。