使用UUID来唯一标识某些东西(我用它来上传到服务器的文件)有多安全?据我所知,它是基于随机数的。然而,我认为在足够长的时间内,仅凭纯粹的偶然性,它最终会重复自身。是否有更好的系统或某种类型的模式来缓解这个问题?
使用UUID来唯一标识某些东西(我用它来上传到服务器的文件)有多安全?据我所知,它是基于随机数的。然而,我认为在足够长的时间内,仅凭纯粹的偶然性,它最终会重复自身。是否有更好的系统或某种类型的模式来缓解这个问题?
非常安全:
据估计,一个人被陨石击中的年度风险是1/170亿,这意味着概率约为0.00000000006(6×10−11),相当于在一年内创建数万亿个UUID并且有一个重复的几率。换句话说,即使在未来100年内每秒生成10亿个UUID,仅有50%的概率会出现一个重复。
注意:
然而,这些概率只适用于使用足够熵量生成UUID的情况。否则,由于统计分散可能较低,重复的概率可能会显著提高。在需要为分布式应用程序提供唯一标识符的情况下,即使合并了来自许多设备的数据,UUID也不会发生冲突,每个设备上使用的种子和生成器的随机性必须可靠地维护整个应用程序的生命周期。如果这不可行,则RFC4122建议改用命名空间变体。
来源:维基百科关于通用唯一标识符的文章中{{link1:随机UUID重复概率部分}}(链接指向2016年12月修改前的版本)。
同时请参阅同一通用唯一标识符文章中关于该主题的{{link2:冲突}}部分。
UUID有不止一种类型,所以“安全性”取决于您使用的 UUID 类型(UUID 规范称之为“版本”)。
版本 1 是基于时间加 MAC 地址的 UUID。128 位包含了48位用于网络卡的MAC地址(由制造商唯一分配)和一个具有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)来生成它们。
如果使用种子不足且周期较小的PRNG来生成UUID,我会说它并不是非常安全。一些随机数生成器也具有较差的方差,即更倾向于某些数字而不是其他数字。这样做效果不好。
因此,它的安全性取决于用于生成它的算法。
另一方面,如果您知道这些问题的答案,那么我认为版本4的UUID应该非常安全。事实上,我正在使用它来标识网络块文件系统中的块,并且到目前为止还没有发生冲突。
在我的情况下,我使用的PRNG是Mersenne Twister,我对其种子的方式非常小心,包括从多个来源包括/dev/urandom获取。Mersenne Twister的周期为2^19937-1。在我看到重复的UUID之前,需要非常非常长的时间。
因此,请选择一个好的库或自己生成,并确保使用一个体面的 PRNG 算法。
1- 如果UUID的唯一性决定了核导弹是否会袭击你国首都,很多你的同胞可能不会被“概率极低”的说法所说服。因此,我使用“几乎全部”这个限定词。
2- 这里有一个哲学问题:是否有任何事情是真正随机的?如果不是,我们怎么知道呢?我们所知道的宇宙是一个模拟吗?是否存在上帝,可能会调整物理规律以改变结果?
3- 如果有人知道这个问题的研究论文,请评论。
引用自维基百科:
因此,任何人都可以创建UUID并将其用于标识某个东西,有合理的把握认为该标识符永远不会被他人意外地用于其他任何事情。
维基百科详细解释了它确实安全的原因。所以回答你的问题:是的,它足够安全。
UUID方案通常不仅使用伪随机元素,还使用当前系统时间和某种通常唯一的硬件ID(如果可用),例如网络MAC地址。
使用UUID的整个意义在于你信任它能够比你自己提供更好的唯一标识符。这与使用第三方加密库而不是自己编写加密算法的原理相同。自己编写可能更有趣,但通常不负责任。
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/>
我已经做了多年。从未遇到问题。
我通常设置我的数据库只有一个包含所有密钥和修改日期等信息的表格。我从来没有遇到过重复键的问题。
唯一的缺点是,当您编写一些查询以快速查找信息时,会大量复制和粘贴密钥。您不再拥有易于记忆的短ID。