Attualmente sto usando guid NEWID()
ma so che non è crittograficamente sicuro.Come posso generare un numero crittograficamente sicuro in SQL Server?
Esiste un modo migliore per generare un numero crittograficamente sicuro in SQL Server?
Attualmente sto usando guid NEWID()
ma so che non è crittograficamente sicuro.Come posso generare un numero crittograficamente sicuro in SQL Server?
Esiste un modo migliore per generare un numero crittograficamente sicuro in SQL Server?
Domanda interessante :)
penso che questo funzionerà: CRYPT_GEN_RANDOM
Oh, questo è anche utilizzabile come fonte di numeri casuali! I numeri casuali sono noiosi in SQL Server. – usr
Anche io ne ho bisogno per essere unico. Posso usare 'Convert (int, CRYPT_GEN_RANDOM (9, Converti (varbinary, NEWID())))'? –
@ user1761123 - forse se potessi inserire * tutti * i tuoi requisiti nella tua domanda, le persone potrebbero avere la possibilità di rispondervi. Finora, sembra che debba essere unico e di dimensione 'int'. Ora, quale definizione di "crittograficamente sicuro" vuoi che usiamo? È questo ad esempio un nonce? –
CRYPT_GEN_RANDOM
è documentato per restituire un "numero casuale di crittografia".
Richiede un parametro di lunghezza compreso tra 1
e 8000
che corrisponde alla lunghezza del numero da restituire in byte.
Per lunghezze < = 8 byte. Questo può essere lanciato su uno dei SQL Server integer types direttamente.
+-----------+------------------+---------+
| 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 |
+-----------+------------------+---------+
Tre di questi sono interi con segno e uno senza segno. Quanto segue utilizzerà ciascuna l'intera gamma dei rispettivi tipi di dati.
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)
È inoltre possibile fornire un valore inferiore rispetto all'archiviazione del tipo di dati.
SELECT CAST(CRYPT_GEN_RANDOM(3) AS INT)
In questo caso è possibile restituire solo numeri positivi. Il bit di segno sarà sempre 0 poiché l'ultimo byte viene considerato come 0x00
. L'intervallo di numeri possibili che possono essere restituiti da quanto sopra è compreso tra 0
e POWER(2, 24) - 1
incluso.
Supponiamo che sia necessario generare un numero casuale tra 1 and 250
.
Un possibile modo di farlo sarebbe
SELECT (1 + CAST(CRYPT_GEN_RANDOM(1) AS TINYINT) % 250) AS X
INTO #T
FROM master..spt_values V1, master..spt_values
Tuttavia questo metodo ha un problema.
SELECT COUNT(*),X
FROM #T
GROUP BY X
ORDER BY X
Le prime dieci righe di risultati sono
+-------+----+
| Count | X |
+-------+----+
| 49437 | 1 |
| 49488 | 2 |
| 49659 | 3 |
| 49381 | 4 |
| 49430 | 5 |
| 49356 | 6 |
| 24914 | 7 |
| 24765 | 8 |
| 24513 | 9 |
| 24732 | 10 |
+-------+----+
numeri più bassi (in questo caso 1 -6
) vengono generati due volte regolarmente come gli altri perché ci sono due possibili ingressi alla funzione modulo che può generare ciascuno di questi risultati.
Una possibile soluzione potrebbe essere quella di eliminare tutti i numeri> = 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)
Questo sembra funzionare sulla mia macchina, ma probabilmente non è garantito che SQL Server valuterà solo la funzione di volta in entrambi i riferimenti a Random
nell'espressione CASE
. Inoltre lascia ancora il problema di aver bisogno di passate seconde e successive per sistemare le righe NULL
in cui il valore casuale è stato scartato.
Dichiarare una UDF scalare può risolvere entrambi questi problemi.
/*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
E poi
UPDATE #T
SET X = dbo.GET_CRYPT_GEN_RANDOM1()
alternativa e più dritto in avanti si potrebbe semplicemente usare
CAST(CRYPT_GEN_RANDOM(8) AS BIGINT) % 250
Sulla base del fatto che la gamma di bigint
è così enorme che ogni pregiudizio sarà probabilmente insignificante . Ci sono 73.786.976.294.838.208 modi che possono essere generati da 1
e 73.786.976.294.838.206 che 249
possono essere dalla query sopra.
Se anche questo piccolo bias non è consentito, è possibile scartare qualsiasi valore NOT BETWEEN -9223372036854775750 AND 9223372036854775749
come illustrato in precedenza.
@Endri Puoi chiarire cosa hai trovato mancante? 'CRYPT_GEN_RANDOM' sembra una risposta perfetta. – CodesInChaos
@CodesInChaos 'CRYPT_GEN_RANDOM' non genera esattamente i numeri .. Naturalmente è possibile convertirli in Int, ma la lunghezza sarà quella richiesta? Ad esempio, se ho bisogno di generare numeri crittograficamente sicuri a 8 cifre, come posso usare 'CRYPT_GEN_RANDOM'? Inoltre, sono unici? –
Inoltre sono curioso di qualsiasi altro modo di generare numeri sicuri in 'SQL'. –