20

現在、guid を使用しNEWID()ていますが、暗号的に安全ではないことがわかっています。

SQL Server で暗号化された安全な番号を生成するより良い方法はありますか?

4

2 に答える 2

26

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 回だけ評価することはおそらく保証されていませんCASENULLさらに、ランダム値が破棄された行を修正するために 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

于 2013-02-06T18:36:10.557 に答える
11

興味深い質問:)

私はこれがうまくいくと思います:CRYPT_GEN_RANDOM

于 2012-12-11T09:52:22.180 に答える