How unique is UUID?
使用UUID唯一标识某个内容(我将其用于上载到服务器的文件)有多安全?据我所知,它是基于随机数。然而,在我看来,只要有足够的时间,它最终会自我重复,纯粹是偶然的。是否有更好的系统或某种类型的模式来缓解这个问题?
非常安全:
the annual risk of a given person being hit by a meteorite is
estimated to be one chance in 17 billion, which means the
probability is about 0.00000000006 (6 × 10?11), equivalent to the odds
of creating a few tens of trillions of UUIDs in a year and having one
duplicate. In other words, only after generating 1 billion UUIDs every
second for the next 100 years, the probability of creating just one
duplicate would be about 50%.
Caveat:
However, these probabilities only hold when the UUIDs are generated
using sufficient entropy. Otherwise, the probability of duplicates
could be significantly higher, since the statistical dispersion might
be lower. Where unique identifiers are required for distributed
applications, so that UUIDs do not clash even when data from many
devices is merged, the randomness of the seeds and generators used on
every device must be reliable for the life of the application. Where
this is not feasible, RFC4122 recommends using a namespace variant
instead.
资料来源:维基百科文章中关于通用唯一标识符的重复部分的随机UUID概率(链接导致从2016年12月开始的修订,然后编辑修改了该部分)。
另请参阅关于同一主题的当前部分,关于同一个通用唯一标识符的文章"冲突"。
如果说"有足够的时间",你的意思是100年,你以每秒10亿的速度创造它们,那么是的,100年后发生碰撞的概率是50%。
有多种类型的UUID,因此"安全性如何"取决于您使用的类型(UUID规范称之为"版本")。
版本1是基于时间的加上MAC地址UUID。128位包含网卡MAC地址的48位(由制造商唯一分配)和分辨率为100纳秒的60位时钟。那个时钟在公元3603年就被包裹起来了,所以这些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的应用程序产生安全影响。
答案可能很大程度上取决于UUID版本。
许多UUID生成器使用版本4随机数。然而,其中许多使用伪随机数生成器来生成它们。
如果用一个小周期的种子不好的prng来生成uuid,我会说它一点也不安全。
因此,它只有和生成它的算法一样安全。
另一方面,如果您知道这些问题的答案,那么我认为版本4 UUID应该是非常安全的。实际上,我使用它来识别网络块文件系统上的块,到目前为止还没有发生冲突。
在我的例子中,我使用的prng是一个mersenne twister,我对它的播种方式非常小心,它来自多个来源,包括/dev/urandom。梅森缠绕机的周期是2^19937?1。我要花很长时间才能看到一个重复的UUID。
引用维基百科:
Thus, anyone can create a UUID and use
it to identify something with
reasonable confidence that the
identifier will never be
unintentionally used by anyone for
anything else
它还详细地解释了它到底有多安全。所以回答你的问题:是的,这是足够安全的。
UUID方案通常不仅使用伪随机元素,而且还使用当前系统时间,以及一些通常唯一的硬件ID(如果可用),例如网络MAC地址。
使用UUID的关键在于,您相信它能够比您自己所能做的更好地提供唯一的ID。这与使用第三方密码库而不是滚动您自己的库是相同的原理。自己动手可能更有趣,但这样做通常不太负责任。
下面是一个测试片段,供您测试它的唯一性。灵感来源于@scalabl3的评论
Funny thing is, you could generate 2 in a row that were identical, of course at mind-boggling levels of coincidence, luck and divine intervention, yet despite the unfathomable odds, it's still possible! :D Yes, it won't happen. just saying for the amusement of thinking about that moment when you created a duplicate! Screenshot video! – scalabl3 Oct 20 '15 at 19:11
如果你觉得幸运,选中复选框,它只检查当前生成的ID。如果你想检查历史记录,不选中它。请注意,如果不选中它,您可能会在某个时刻用完RAM。我试图让它对CPU友好,这样你可以在需要时快速中止,只需再次点击运行代码片段按钮或离开页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | 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); } } }; }; |
1 2 | 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,当然对于您的用途也是如此。
但假设他们不是。
Is there a better system or a pattern of some type to alleviate this issue?
以下是几种方法:
使用更大的UUID。例如,使用256或512或…如果您有一个可靠的entropy2源,那么添加到类型4样式UUID中的每一位都将减少一半的碰撞概率。
构建一个集中式或分布式服务,生成UUID,并记录它所发布的每一个UUID。每次生成一个新的UUID时,它都会检查以前从未发出过UUID。如果我们假设运行服务的人绝对值得信赖、廉洁等等,这样的服务在技术上就可以直接实现(我认为)。不幸的是,他们不是……尤其是当政府有干预的可能时。因此,这种方法可能是不切实际的,在现实世界中可能是不可能的。
1-如果UUID的独特性决定了核导弹是否在贵国首都发射,那么许多贵国公民不会相信"可能性极低"。因此我的"几乎全部"资格。
这是一个哲学问题。有什么是真正随机的吗?我们怎么知道是不是?我们所知道的宇宙是模拟的吗?有没有一个上帝可以想象地"调整"物理定律来改变一个结果?
3-如果有人知道关于这个问题的任何研究论文,请发表评论。
我不知道这对您是否重要,但请记住,guid是全局唯一的,但guid的子字符串不是。