跨多个客户端和服务器生成真正全局唯一的ID

13

概述

如何在Flash和/或JavaScript客户端生成真正的全局唯一标识符?我是否可以使用当前浏览器/Flash中可用的随机数生成器(RNG)来实现,还是必须使用服务器端随机性构建组合ID?

详情

我需要为对象生成全局唯一标识符。 我有多个使用Java编写的服务器端“系统”,它们需要能够交换 ID;每个系统还具有一组Flex / JavaScript客户端,这些客户端实际上为新对象生成ID。 我需要确保在一组不相关的系统中具有全局唯一性;例如,我需要能够合并/同步两个独立系统的数据库。 我必须保证这些ID之间永远不会发生冲突,并且我永远不需要更改创建后的对象的ID。 我需要能够在Flash和JavaScript客户端中生成ID,而无需针对每个ID联系服务器。 只要不经常与服务器联系,就可以依赖某些由服务器提供的种子或系统ID的解决方案。 完全断开连接的解决方案更可取。 同样,不需要事先注册系统的解决方案比依赖中央管理机构(例如MAC地址中的OUI)的解决方案更可取。

我知道显而易见的解决方案是“使用UUID生成器”,例如Flash中的UIDUtil。 但是,该函数明确声明不具备全局唯一性。 总的来说,我担心依赖PRNG保证全局唯一性。

建议的解决方案

完全依靠客户端中的安全随机数生成器。

Flash 11+具有flash.crypto.generateRandomBytes; JavaScript具有window.crypto,但它相当新,并且在IE中不受支持。 还有像sjcl这样的解决方案,它使用鼠标添加熵。

我知道如果有一个完美的随机数生成器,2122的随机UID碰撞的可能性非常小,但我担心在javascript或flash客户端中实际上无法获得这种程度的随机性。我进一步担心即使是加密随机数生成器的典型用例也与我的不同:对于会话密钥等,只要攻击者无法预测,碰撞就是可以接受的。在我的情况下,碰撞是完全不可接受的。我真的应该依靠安全随机数生成器的原始输出来生成唯一ID吗?

生成一个包含系统、会话和对象ID的复合ID。

一个明显的实现方法是在服务器安装时创建系统UUID,在每个客户端登录时保留一个会话ID(例如在数据库中),然后将系统和会话ID发送到客户端,客户端将保留一个每个会话的计数器。UID将是三元组:系统ID、会话ID、客户端计数器。

我可以想象直接串联它们或使用加密哈希函数对它们进行哈希。我担心哈希本身可能会引入碰撞,特别是如果哈希的输入与输出大小相近。但哈希将掩盖系统ID和计数器,这可能会泄露信息。

另一种解决方案是在安装时生成系统ID,或者使用类似DOI的中央注册表来分配唯一的系统ID。然而,这需要更多的协调,但我想这是真正保证全球唯一性的唯一方式。

关键问题

  • 基于随机还是复合?
  • 是否包含系统ID?
  • 如果包含系统ID:生成一个随机的系统ID还是使用中央注册表?
  • 包含时间戳或其他nonce吗?
  • 要哈希还是不要哈希?

我正在解决一个类似的问题。我在这里发布了相关信息(http://stackoverflow.com/questions/23148547/strategy-for-generating-unique-and-secure-identifiers-for-use-in-a-sometimes-of)。我很想知道你最终采用了什么解决方案以及它的效果如何。 - herbrandson
4个回答

4
最简单的方法是使用由服务器分配的客户端ID,每个客户端都会递增一个值,对于该客户端上的每个片段也会递增一个值。客户端ID和片段ID成为该内容的全局唯一标识符。
另一种简单的方法是在服务器上生成一组唯一的ID(例如每次2k),并将它们批量发送给每个客户端。当客户端用完ID时,它会联系服务器以获取更多ID。
客户端ID应存储在可供所有服务器访问的中央存储库中。
可能有助于查看分布式哈希方法,该方法用于唯一标识和定位点对点环境中的片段。考虑到您有一个可以干预确保唯一性的服务器,这可能过于复杂了。
要回答您的问题,您需要确定系统ID、nonce或哈希的附加复杂性所带来的好处。

系统标识: 系统标识通常用于在域内唯一地标识系统。因此,如果您不关心用户是谁,或者有多少个会话处于打开状态,但只想确保知道设备是谁,则使用系统标识。这在以用户为中心的环境(如JavaScript或Flash)中通常没有太大用处,因为用户或会话可能是相关的。

Nonce: Nonce / salt / random seed 用于混淆或其他方式来改变ID。当您不希望他人能够猜测ID的原始值时,这一点很重要。如果需要这样做,则最好使用私有加密密钥加密ID,并向每个需要读取ID的使用者传递公共解密密钥。

时间戳: 考虑到客户端时钟的可变性(即您无法保证它遵循任何时间或时区),时间戳需要被视为该应用程序的伪随机值。

哈希: 哈希经常被(滥)用来创建唯一的键,但它们的真正目的是将一个大的(可能是无限的)域映射到一个更小、更易于管理的域。例如,MD5通常用于从时间戳、随机数和/或nonce数据生成唯一ID。实际上发生的是,MD5函数将一个无限范围的数据映射到了一个2^128个可能性的空间中。虽然这是一个巨大的空间,但它并不是无限的,所以逻辑告诉你,即使只是在理论上,也会有相同的哈希分配给两个不同的片段。另一方面,完美哈希试图为每个数据片段分配一个唯一的标识符,但如果您只是从一开始就为每个客户端片段分配一个唯一的标识符,这是完全不必要的。

2
一个中间地带建立在 @ping 的答案之上:
  1. 使用客户端名称、高分辨率时间和可选的其他伪随机种子
  2. 对数据进行哈希处理以生成 UID(或者直接使用 UUID)
  3. 将结果记录到中央服务器以输入数据库
  4. 将任何冲突视为明显标记的错误,而不是需要特殊代码的情况。

使用 UUID 或足够长的哈希值,重复的机会很小。因此:

A) 应用程序的生命周期内没有重复项,一切正常。 B) 在几十年的时间里,您可能会看到一个或两个重复项(奇怪!)。手动干预来处理这些情况;如果您正在运行带有客户端的服务器,则可以承受它。 C) 如果出现第三次冲突,则代码存在根本性问题,并且可以调查并采取措施避免重复发生。

这样,ID 是在客户端生成的,与服务器的联系是单向的且操作上不重要的,种子不必是随机的,哈希处理模糊了 ID 的起源,从而避免了构造冲突,并且您可以确信没有发生冲突。 (如果您测试了冲突检测代码!)即使在这种情况下,UUID 也足够适用。

哈希处理增加冲突的可能性的唯一方法是如果原始种子信息中的信息内容接近哈希值的大小。这非常不可能,但如果确实如此,并且您仍然考虑微陨石,则只需增加哈希值的大小即可。


2

有一种快速且可能不适用于您的用例的方法是使用Java的UUID,并将其与客户端名称之类的东西相结合。这应该解决“多个客户端和多个服务器”问题。

背后的原理是获取相同纳秒内的2个调用的可能性很低,请参考下面提供的链接。现在,通过将客户端名称与UUID耦合,您可以确保跨客户端具有唯一ID,这样只需要处理同一个客户端在同一个纳秒内调用两次的用例。

您可以编写一个Java模块来生成ID,然后让Flash与此模块通信。作为参考,您可以参考--
使用UUID生成唯一ID真的是唯一的吗?
让Java和Flash相互通信


1

我的建议是,每个服务器锁定一个数据库表并从中获取一个ID,然后递增它。这将成为服务器的唯一ID。

每个连接的客户端将获取此ID,并与服务器颁发的唯一标识符相结合。此唯一密钥必须对该服务器唯一,但另一个服务器可能会向不同的客户端颁发相同的ID。

最后,每个客户端将为每个请求生成一个唯一ID。

将所有三个内容结合起来将保证整个系统的真正唯一全局ID,最终的ID将类似于:

[server id][client id][request id]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接