現在、guid を使用しNEWID()
ていますが、暗号的に安全ではないことがわかっています。
SQL Server で暗号化された安全な番号を生成するより良い方法はありますか?
現在、guid を使用しNEWID()
ていますが、暗号的に安全ではないことがわかっています。
SQL Server で暗号化された安全な番号を生成するより良い方法はありますか?
CRYPT_GEN_RANDOM
「暗号乱数」を返すことが文書化されています。
1
との間の長さパラメータを使用8000
します。これは、返される数値の長さ (バイト単位) です。
長さ <= 8 バイトの場合。これは、 SQL Server 整数型の 1 つに直接キャストできます。
+-----------+------------------+---------+
| 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 |
+-----------+------------------+---------+
そのうちの 3 つは符号付き整数で、1 つは符号なしです。以下はそれぞれ、それぞれのデータ型の全範囲を使用します。
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
。
それを行う1つの可能な方法は
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
結果の最初の 10 行は
+-------+----+
| Count | X |
+-------+----+
| 49437 | 1 |
| 49488 | 2 |
| 49659 | 3 |
| 49381 | 4 |
| 49430 | 5 |
| 49356 | 6 |
| 24914 | 7 |
| 24765 | 8 |
| 24513 | 9 |
| 24732 | 10 |
+-------+----+
低い数値 (この場合1 -6
は ) は、他の数値の 2 倍の規則性で生成されます。これらの各結果を生成できるモジュラス関数への 2 つの可能な入力があるためです。
考えられる解決策の 1 つは、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)
Random
これは私のマシンで機能しているように見えますが、SQL Server が式内の両方の参照で関数を 1 回だけ評価することはおそらく保証されていませんCASE
。NULL
さらに、ランダム値が破棄された行を修正するために 2 回目以降のパスが必要になるという問題が残っています。
スカラー 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
が非常に大きいため、バイアスはおそらく重要ではないという理由で。生成できるウェイは 73,786,976,294,838,208 あり、上記のクエリから1
生成できるウェイは 73,786,976,294,838,206 です。249
そのわずかな偏りが許容されない場合でも、前に示したように値を破棄できますNOT BETWEEN -9223372036854775750 AND 9223372036854775749
。
興味深い質問:)
私はこれがうまくいくと思います:CRYPT_GEN_RANDOM