CRYPT_GEN_RANDOM足够独特吗?

时间:2017-09-05 14:07:58

标签: sql-server uniqueidentifier

我正在使用此

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

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

使用它是个好主意吗?

1 个答案:

答案 0 :(得分:4)

以这种方式生成的“GUID”不是真正的GUID,因为保留用于指示GUID的变体和版本的GUID中的位也将是随机的。在实践中,大多数工具根本不关心或查看GUID的位,但可以想象某些系统或未来版本出现GUID位模式的问题无效。碰撞不是你必须担心的,因为剩下的部分在你的结尾是随机的,但这仍然是丑陋的。

对于(加密)随机GUID,您需要一个版本为4的GUID,变量1.四个位表示版本,两个表示变量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 Server对它们的编码是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的大小相同,因此存储也不是问题。 (但您可能希望考虑使用字符串列,以便将来为新的令牌格式留出空间。)