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.
Interesante pregunta :)
Creo que esto funcionará: CRYPT_GEN_RANDOM