password encrypted encrypt column sql sql-server cryptography

encrypted - sql server encryption



¿Cómo puedo generar un número criptográficamente seguro en SQL Server? (2)

Actualmente estoy usando guid NEWID() pero sé que no es criptográficamente seguro.

¿Hay alguna forma mejor de generar un número criptográficamente seguro en SQL Server?


CRYPT_GEN_RANDOM está documentado para devolver un "número aleatorio criptográfico".

Se necesita un parámetro de longitud entre 1 y 8000 que es la longitud del número a devolver en bytes.

Para longitudes <= 8 bytes. Esto se puede convertir directamente en uno de los tipos enteros de SQL Server .

+-----------+------------------+---------+ | 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 | +-----------+------------------+---------+

Tres de ellos son enteros con signo y uno sin firmar. Los siguientes usarán cada uno el rango completo de sus respectivos tipos de datos.

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)

También es posible proporcionar un valor más corto que el almacenamiento de tipo de datos.

SELECT CAST(CRYPT_GEN_RANDOM(3) AS INT)

En este caso, solo se pueden devolver números positivos. El bit de signo siempre será 0, ya que el último byte se trata como 0x00 . El rango de números posibles que puede devolver el anterior es entre 0 y POWER(2, 24) - 1 inclusive.

Supongamos que el requisito es generar un número aleatorio entre 1 and 250 .

Una posible forma de hacerlo sería

SELECT ( 1 + CAST(CRYPT_GEN_RANDOM(1) AS TINYINT) % 250) AS X INTO #T FROM master..spt_values V1, master..spt_values

Sin embargo, este método tiene un problema.

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

Las primeras diez filas de resultados son

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

Los números más bajos (en este caso 1 -6 ) se generan dos veces más regularmente que los otros porque hay dos entradas posibles a la función del módulo que pueden generar cada uno de esos resultados.

Una posible solución sería descartar todos los números> = 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)

Esto parece funcionar en mi máquina, pero probablemente no esté garantizado que SQL Server solo evalúe la función una vez en ambas referencias a Random en la expresión CASE . Además, aún deja el problema de necesitar pases secundarios y subsiguientes para reparar las filas NULL donde se descartó el valor aleatorio.

Declarar un UDF escalar puede resolver ambos problemas.

/*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

Y entonces

UPDATE #T SET X = dbo.GET_CRYPT_GEN_RANDOM1()

Alternativamente y más directamente uno podría simplemente usar

CAST(CRYPT_GEN_RANDOM(8) AS BIGINT) % 250

Sobre la base de que el rango de bigint es tan grande que cualquier sesgo probablemente será insignificante. Hay 73,786,976,294,838,208 formas en que se puede generar 1 y 73,786,976,294,838,206 que 249 pueden ser de la consulta anterior.

Si incluso ese pequeño posible sesgo no está permitido, puede descartar cualquier valor NOT BETWEEN -9223372036854775750 AND 9223372036854775749 como se muestra anteriormente.