我目前正在使用guid NEWID()
,但我知道它不具有加密安全性。
有没有更好的方法在SQL Server中生成加密安全号码?
答案 0 :(得分:26)
CRYPT_GEN_RANDOM
以返回“加密随机数”。
它需要1
和8000
之间的长度参数,它是以字节为单位返回的数字的长度。
长度<= 8字节。这可以直接转换为SQL Server integer types中的一个。
+-----------+------------------+---------+
| Data type | Range | Storage |
+-----------+------------------+---------+
| bigint | -2^63 to 2^63-1 | 8 Bytes |
| int | -2^31 to 2^31-1 | 4 Bytes |
| smallint | -2^15 to 2^15-1 | 2 Bytes |
| tinyint | 0 to 255 | 1 Byte |
+-----------+------------------+---------+
其中三个是有符号整数,一个是无符号整数。以下各项将使用各自数据类型的全部范围。
SELECT
CAST(CRYPT_GEN_RANDOM(1) AS TINYINT),
CAST(CRYPT_GEN_RANDOM(2) AS SMALLINT),
CAST(CRYPT_GEN_RANDOM(4) AS INT),
CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)
也可以提供比数据类型存储更短的值。
SELECT CAST(CRYPT_GEN_RANDOM(3) AS INT)
在这种情况下,只能返回正数。符号位始终为0,因为最后一个字节被视为0x00
。上述可返回的可能数字范围介于0
和POWER(2, 24) - 1
之间。
假设要求是在1 and 250
之间生成一些随机数。
一种可行的方法是
SELECT ( 1 + CAST(CRYPT_GEN_RANDOM(1) AS TINYINT) % 250) AS X
INTO #T
FROM master..spt_values V1, master..spt_values
但是这种方法存在问题。
SELECT COUNT(*),X
FROM #T
GROUP BY X
ORDER BY X
前十行结果是
+-------+----+
| Count | X |
+-------+----+
| 49437 | 1 |
| 49488 | 2 |
| 49659 | 3 |
| 49381 | 4 |
| 49430 | 5 |
| 49356 | 6 |
| 24914 | 7 |
| 24765 | 8 |
| 24513 | 9 |
| 24732 | 10 |
+-------+----+
较小的数字(在这种情况下为1 -6
)的生成次数是其他数字的两倍,因为模数函数有两个可能的输入,可以生成每个结果。
一种可能的解决方案是丢弃所有数字&gt; = 250
UPDATE #T
SET X = CASE
WHEN Random >= 250 THEN NULL
ELSE ( 1 + Random % 250 )
END
FROM #T
CROSS APPLY (SELECT CAST(CRYPT_GEN_RANDOM(1) AS TINYINT)) CA (Random)
这似乎适用于我的计算机,但可能无法保证SQL Server只会在Random
表达式中对CASE
的两个引用中评估该函数一次。此外,它仍然存在需要第二次和后续传递来修复随机值被丢弃的NULL
行的问题。
声明标量UDF可以解决这两个问题。
/*Work around as can't call CRYPT_GEN_RANDOM from a UDF directly*/
CREATE VIEW dbo.CRYPT_GEN_RANDOM1
AS
SELECT CAST(CRYPT_GEN_RANDOM(1) AS TINYINT) AS Random
go
CREATE FUNCTION GET_CRYPT_GEN_RANDOM1()
RETURNS TINYINT
AS
BEGIN
DECLARE @Result TINYINT
WHILE (@Result IS NULL OR @Result >= 250)
/*Not initialised or result to be discarded*/
SELECT @Result = Random FROM dbo.CRYPT_GEN_RANDOM1
RETURN @Result
END
然后
UPDATE #T
SET X = dbo.GET_CRYPT_GEN_RANDOM1()
或者更直接地可以使用
CAST(CRYPT_GEN_RANDOM(8) AS BIGINT) % 250
理由是bigint
的范围如此之大,以至于任何偏见都可能是微不足道的。可以生成1
的方法有73,786,976,294,838,208种,249
可以从上面的查询中生成73,786,976,294,838,206。
如果不允许那么小的可能偏差,您可以丢弃任何值NOT BETWEEN -9223372036854775750 AND 9223372036854775749
,如前所示。
答案 1 :(得分:11)
有趣的问题:)
我认为这样可行:CRYPT_GEN_RANDOM