2012-12-11 20 views
21

私は現在guid NEWID()を使用していますが、暗号的に安全でないことがわかります。SQL Serverで暗号で安全な番号を生成するにはどうすればよいですか?

SQL Serverで暗号で安全な番号を生成する方法はありますか?

+0

@Endriあなたは欠けていることを明確にすることはできますか? 'CRYPT_GEN_RANDOM'は完全に良い答えのようです。 – CodesInChaos

+0

@CodesInChaos 'CRYPT_GEN_RANDOM'は正確な数値を生成しません。もちろん、それらをIntに変換できますが、長さは必要なものになりますか?たとえば、8桁の暗号で安全な番号を生成する必要がある場合は、どのように 'CRYPT_GEN_RANDOM'を使用できますか?また、彼らはユニークですか? –

+0

また、私は 'SQL'で安全な数値を生成する他の方法についても興味があります。 –

答えて

11

興味深い質問:)

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

+0

ああ、これは乱数源としても使える! SQL Serverでは乱数が面倒です。 – usr

+0

私はそれもユニークである必要があります。 'Convert(int、CRYPT_GEN_RANDOM(9、Convert(varbinary、NEWID()))')を使うことはできますか? –

+0

@ user1761123 - おそらくあなたの質問のすべての*あなたの要件の*を置くことができた場合、人々はそれに答える機会があります。今のところ、それは一意で、 'int'サイズである必要があるようです。さて、「暗号的に安全」という定義は、私たちが使いたいと思っているものですか?これは例えばですか?ノンス? –

26

CRYPT_GEN_RANDOMは、 "暗号の乱数" を返すように文書化されています。

18000の間の長さパラメータを返します。これはバイト単位で返される数値の長さです。

長さは< = 8バイトです。これは直接SQL Server integer typesの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) 

この場合、正の数だけを返すことができます。最後のバイトが0x00として扱われるので、符号ビットは常に0になります。上記によって返される可能な数値の範囲は、0POWER(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 

だろう

しかし、この方法では問題があります。生成することができるモジュラス関数への2つの可能な入力があるため、結果の

SELECT COUNT(*),X 
FROM #T 
GROUP BY X 
ORDER BY X 

最初の10行は、他のものとして、定期的に2回発生している(この場合1 -6に)

+-------+----+ 
| Count | X | 
+-------+----+ 
| 49437 | 1 | 
| 49488 | 2 | 
| 49659 | 3 | 
| 49381 | 4 | 
| 49430 | 5 | 
| 49356 | 6 | 
| 24914 | 7 | 
| 24765 | 8 | 
| 24513 | 9 | 
| 24732 | 10 | 
+-------+----+ 

低い数値でありますそれぞれの結果

一つの可能​​な解決策は、すべての数字を破棄するだろう> = 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を修正するために、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() 

あるいはより真っ直ぐ前方一つは単にbigintの範囲は、任意のバイアスはおそらく無意味になるように巨大であるという理由で

CAST(CRYPT_GEN_RANDOM(8) AS BIGINT) % 250 

を使用でき。 1が生成できる73,786,976,294,838,208の方法があり、上記のクエリから73,786,976,294,838,206,249を得ることができます。

バイアスを小さくしても許されない場合は、前述のように値NOT BETWEEN -9223372036854775750 AND 9223372036854775749を破棄することができます。

関連する問題