¿Números aleatorios seguros en javascript?

80

¿Cómo puedo generar números aleatorios criptográficamente seguros en javascript?

Kyle
fuente
3
¿Qué quiere decir exactamente con "criptográficamente"? Utilice Math.random () para devolver un número aleatorio entre 0 y 1. Es técnicamente pseudoaleatorio, ya que en realidad no existe ninguna forma (sencilla) de generar números aleatorios verdaderos.
Artista lógico
26
Creo que la pregunta es pedir un generador de números aleatorios que sea adecuado para la criptografía. P.ej. El módulo RND implementado por defecto en muchos idiomas no sería adecuado.
winwaed el
11
@logic - en.wikipedia.org/wiki/…
Kyle
18
@Logic Artist: no, Math.random no es criptográficamente seguro. Criptográficamente seguro es un término estándar que significa que el valor es impredecible, incluso para un adversario que está dispuesto a invertir una cantidad significativa de tiempo y energía tratando de predecirlo o distinguirlo del azar.
DW
También vea Aleatoriedad insegura
SyntaxRules

Respuestas:

24

Por ejemplo, puede usar el movimiento del mouse como semilla para números aleatorios, leer el tiempo y la posición del mouse cada vez que ocurre el evento onmousemove, alimentar esos datos a una función de blanqueamiento y tendrá algo de primera clase al azar a mano. Sin embargo, asegúrese de que el usuario haya movido el mouse lo suficiente antes de usar los datos.

Editar: yo mismo jugué un poco con el concepto al hacer un generador de contraseñas, no garantizaría que mi función de blanqueamiento sea impecable, pero al estar constantemente resembrado estoy bastante seguro de que es suficiente para el trabajo: ebusiness.hopto.org /generator.htm

Edit2: ahora funciona con teléfonos inteligentes, pero solo al deshabilitar la funcionalidad táctil mientras se recopila la entropía. Android no funcionará correctamente de ninguna otra manera.

aaaaaaaaaaaa
fuente
11
Aquí hay una biblioteca de cifrado con una licencia BSD y un generador de números aleatorios: crypto.stanford.edu/sjcl
aaaaaaaaaaaa
Eso parece cumplir con los requisitos de los OP.
Presidente James K. Polk
SJCL (la biblioteca de cifrado de Stanford) parece una excelente opción. Tienen un artículo publicado que describe en detalle cómo generan números aleatorios criptográficamente, y su enfoque parece sólido y bien pensado.
DW
Tengo una sugerencia de comercio electrónico: agregue un campo delimitador que haría que esa cadena se insertara entre cada .password spanetiqueta para facilitar la copia / pegado / manipulación. Por ejemplo, actualmente, si copio y pego las cadenas generadas, se pegarán como una cadena larga.
trusktr
Tenga en cuenta que esto no funciona en una plataforma móvil, ya que no hay mouse, por lo que aquellos que usan esta función necesitarán una fuente de números aleatorios alternativa.
Matt Eskridge
61

Ha habido una discusión en WHATWG sobre agregar esto al objeto window.crypto. Puede leer la discusión y consultar la API propuesta y el error de webkit (22049).

Acabo de probar el siguiente código en Chrome para obtener un byte aleatorio:

(function(){
  var buf = new Uint8Array(1);
  window.crypto.getRandomValues(buf);
  alert(buf[0]);
})();

Pablo V
fuente
Funciona en IE 11 si reemplaza window.cryptocon window.msCrypto.
Michael Kropat
28

En orden, creo que tus mejores apuestas son:

  1. window.crypto.getRandomValues ​​o window.msCrypto.getRandomValues
  2. La función randomWords de la biblioteca sjcl ( http://crypto.stanford.edu/sjcl/ )
  3. El generador de números aleatorios de la biblioteca isaac (que es sembrado por Math.random, por lo que no es realmente seguro criptográficamente) ( https://github.com/rubycon/isaac.js )

window.crypto.getRandomValues ​​se ha implementado en Chrome desde hace un tiempo, y hace relativamente poco tiempo también en Firefox. Desafortunadamente, Internet Explorer 10 y versiones anteriores no implementan la función. IE 11 tiene window.msCrypto, que logra lo mismo. sjcl tiene un gran generador de números aleatorios derivado de los movimientos del mouse, pero siempre existe la posibilidad de que el mouse no se haya movido lo suficiente para generar el generador, o que el usuario esté en un dispositivo móvil donde no haya ningún movimiento del mouse. Por lo tanto, recomiendo tener un caso alternativo en el que aún pueda obtener un número aleatorio no seguro si no hay otra opción. Así es como he manejado esto:

function GetRandomWords (wordCount) {
    var randomWords;

    // First we're going to try to use a built-in CSPRNG
    if (window.crypto && window.crypto.getRandomValues) {
        randomWords = new Int32Array(wordCount);
        window.crypto.getRandomValues(randomWords);
    }
    // Because of course IE calls it msCrypto instead of being standard
    else if (window.msCrypto && window.msCrypto.getRandomValues) {
        randomWords = new Int32Array(wordCount);
        window.msCrypto.getRandomValues(randomWords);
    }
    // So, no built-in functionality - bummer. If the user has wiggled the mouse enough,
    // sjcl might help us out here
    else if (sjcl.random.isReady()) {
        randomWords = sjcl.random.randomWords(wordCount);
    }
    // Last resort - we'll use isaac.js to get a random number. It's seeded from Math.random(),
    // so this isn't ideal, but it'll still greatly increase the space of guesses a hacker would
    // have to make to crack the password.
    else {
        randomWords = [];
        for (var i = 0; i < wordCount; i++) {
            randomWords.push(isaac.rand());
        }
    }

    return randomWords;
};

Deberá incluir sjcl.js e isaac.js para esa implementación, y asegúrese de iniciar el recopilador de entropía sjcl tan pronto como se cargue su página:

sjcl.random.startCollectors();

sjcl es BSD y GPL con licencia dual, mientras que isaac.js es MIT, por lo que es perfectamente seguro usar cualquiera de ellos en cualquier proyecto. Como se mencionó en otra respuesta, clipperz es otra opción, sin embargo, por alguna extraña razón, tiene licencia de AGPL. Todavía tengo que ver a alguien que parezca entender qué implicaciones tiene para una biblioteca de JavaScript, pero lo evitaría universalmente.

Una forma de mejorar el código que publiqué podría ser almacenar el estado del generador de números aleatorios isaac en localStorage, para que no se reinicie cada vez que se cargue la página. Isaac generará una secuencia aleatoria, pero para fines de criptografía, la semilla es de suma importancia. Sembrar con Math.random es malo, pero al menos un poco menos malo si no está necesariamente en cada carga de página.

ZeroG
fuente
Seguí este enfoque en github.com/simbo1905/srp-6a-demo/blob/master/srp/Client/lib/… para crear un número hexadecimal aleatorio de 128. Los usuarios son window.crypto else isaac. Si tiene que usar isaac, calentará la carga de la página del generador omitiendo los randoms durante 0.1 s. El campo de entrada de texto onkeyup in también lo hace random16byteHex.advance(Math.floor(event.keyCode/4));para omitir números aleatorios durante algunos milisegundos. Eso haría que los randoms de isaac utilizados en esa aplicación de navegador dependan de la entrada del usuario y la velocidad del hardware / navegador sea muy difícil de adivinar.
simbo1905
1
@ZeroG Con respecto a su comentario sobre SJCL: "siempre existe la posibilidad de que el mouse no se haya movido lo suficiente para sembrar el generador, o que el usuario esté en un dispositivo móvil donde no haya ningún movimiento del mouse" . Ahora funciona bien en dispositivos móviles, porque la entropía ahora se recopila de touchmove( extracción # 151 ) y devicemotion( extracción # 79 ).
TachyonVortex
1
parece que sjcl ya usa window.crypto está disponible
Ales
14

Utilice window.crypto.getRandomValues, así:

var random_num = new Uint8Array(2048 / 8); // 2048 = number length in bits
window.crypto.getRandomValues(random_num);

Esto es compatible con todos los navegadores modernos y utiliza el generador aleatorio del sistema operativo (por ejemplo /dev/urandom). Si necesita compatibilidad con IE11, debe usar su implementación prefijada a través de var crypto = window.crypto || window.msCrypto; crypto.getRandomValues(..).

Tenga en cuenta que la window.cryptoAPI también puede generar claves directamente , lo que puede ser la mejor opción.

Phihag
fuente
Creo que te refieres a Uint8Array (revisa la ortografía)
Flyingkiwi
1
¿Es "longitud de clave" el término correcto aquí? ¿Y las longitudes de las claves no están denominadas en bits?
Indolering
1
¿Cómo uso window.crypto.getRandomValues ​​si quiero generar números aleatorios en un rango particular, digamos 4000-64000 y necesito 1 número aleatorio cada vez?
Sid
2
@ Sid Eso suena como una excelente pregunta. ¡Pregúntalo !
phihag
2
@phihag: ya lo hice, no hay respuestas hasta ahora. stackoverflow.com/questions/41437492/…
Sid
6

para obtener un número fuerte criptográfico del rango [0, 1)(similar a Math.random()) use crypto :

let random = ()=> crypto.getRandomValues(new Uint32Array(1))[0]/2**32;

console.log( random() );

Kamil Kiełczewski
fuente
4

Es posible que desee probar http://sourceforge.net/projects/clipperzlib/ Tiene una implementación de Fortuna, que es un generador de números aleatorios criptográficamente seguro. (Eche un vistazo a src / js / Clipperz / Crypto / PRNG.js). Parece que también utiliza el ratón como fuente de aleatoriedad.

ameer
fuente
Información más detallada sobre la biblioteca está disponible aquí clipperz.com/open_source/javascript_crypto_library
ameer
1
Buena respuesta, desafortunadamente tiene licencia de AGPL que no creo que sea compatible con mi proyecto.
Kyle
A partir del 2 de mayo de 2014, Clipperz se trasladó de AGPL a BSD en la confirmación c9f12e87c7ac88e4612de4d1d70df7c53f77e2ad
GGG
1

En primer lugar, necesita una fuente de entropía. Por ejemplo, movimiento del mouse, contraseña o cualquier otro. Pero todas estas fuentes están muy lejos de ser aleatorias y le garantizan 20 bits de entropía, rara vez más. El siguiente paso que debe realizar es utilizar un mecanismo como "KDF basado en contraseña", lo que dificultará computacionalmente la distinción entre datos y datos aleatorios.

usuario2674414
fuente
0

Hace muchos años, tuvo que implementar su propio generador de números aleatorios y sembrarlo con la entropía recopilada por el movimiento del mouse y la información de tiempo. Esta fue la era Phlogiston de la criptografía JavaScript. En estos días tenemos window.cryptoque trabajar.

Si necesita un número entero aleatorio , random-number-csprng es una excelente opción. Genera de forma segura una serie de bytes aleatorios y luego los convierte en un entero aleatorio imparcial.

const randomInt = require("random-number-csprng");
(async function() {
    let random = randomInt(10, 30);
    console.log(`Your random number: ${random}`);
})();

Si necesita un número de punto flotante aleatorio, tendrá que trabajar un poco más. Sin embargo, generalmente, la aleatoriedad segura es un problema de números enteros, no un problema de coma flotante.

Scott Arciszewski
fuente