CRYPT_GEN_RANDOM 够独特吗?

Is CRYPT_GEN_RANDOM enough unique?

我正在使用这个

生成一个令牌
SET @Token = CAST(CRYPT_GEN_RANDOM(16) AS UNIQUEIDENTIFIER)

令牌将在 URL 中可见,并且应该是唯一的。

使用它是个好主意吗?

您以这种方式生成的"GUID"不是真正的 GUID,因为 GUID 中保留的用于指示 GUID 的变体和版本的位也是随机的。实际上,大多数工具根本不关心或查看 GUID 的位,但可以想象某些系统或未来版本 有 GUID 问题无效的位模式。碰撞不是你必须担心的事情,因为剩下的位在你这边是随机的,但这仍然很难看。

对于(加密的)随机 GUID,您需要版本 4、变体 1 的 GUID。四位表示版本,两位表示变体 1,剩下 122 个随机位,应该足够了。在 T-SQL 中正确设置这些位并不直观,但可行:

SELECT CONVERT(UNIQUEIDENTIFIER,
    CRYPT_GEN_RANDOM(7) + 
    -- Set version = 4
    CONVERT(BINARY(1), (CRYPT_GEN_RANDOM(1) & 15) | 64) +
    -- Set variant = 1
    CONVERT(BINARY(1), (CRYPT_GEN_RANDOM(1) & 63) | 128) +
    CRYPT_GEN_RANDOM(7) 
)

位和字节的位置不直观,因为SQL服务器对它们的编码是weird

同时考虑以下备选方案:

  1. 使用简单的 NEWID() 作为令牌,几乎不用担心攻击者可能会猜测下一个值,或者如果他们这样做也不会危及安全性。请记住,这通常至少需要能够随意生成 GUID 块,最坏的情况是访问生成 GUID 的机器的内存。前者可以限制速率,后者意味着它可能已经结束了。
  2. 使用随机字节,不假装它是 GUID,并将它们转换为十六进制字符串,如通过(例如)SELECT CONVERT(CHAR(32), CRYPT_GEN_RANDOM(16), 2) 获得的(使用 SELECT CONVERT(BINARY(16), @s, 2) 转换回来)。字符串比 GUID 更容易传递,因此很难理解为什么您在这里绝对需要 GUID。您还可以获得 6 个完整的随机性位。 BINARY(16) 列的大小与 UNIQUEIDENTIFIER 相同,因此存储也不是问题。 (但您可能还是希望考虑使用字符串列,以便在将来为新的标记格式留出空间。)