101 votes

Sécuriser les nombres aléatoires en javascript ?

Comment générer des nombres aléatoires cryptographiquement sécurisés en javascript ?

73voto

Paul V Points 635

Une discussion a eu lieu au WHATWG sur l'ajout de cet élément à l'objet window.crypto. Vous pouvez lire la discussion et consultez le Proposition d'API et le bogue webkit (22049).

Je viens de tester le code suivant dans Chrome pour obtenir un octet aléatoire :

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

30voto

ZeroG Points 101

Dans l'ordre, je pense que vos meilleurs paris sont :

  1. window.crypto.getRandomValues ou window.msCrypto.getRandomValues
  2. La fonction randomWords de la bibliothèque sjcl ( http://crypto.stanford.edu/sjcl/ )
  3. Le générateur de nombres aléatoires de la bibliothèque isaac (qui est ensemencé par Math.random, donc pas vraiment sécurisé sur le plan cryptographique) ( https://github.com/rubycon/isaac.js )

window.crypto.getRandomValues a été implémenté dans Chrome depuis un certain temps maintenant, et relativement récemment dans Firefox également. Malheureusement, Internet Explorer 10 et les versions antérieures ne mettent pas en œuvre cette fonction. IE 11 dispose de window.msCrypto, qui accomplit la même chose. sjcl dispose d'un excellent générateur de nombres aléatoires alimenté par les mouvements de la souris, mais il y a toujours une chance que la souris n'ait pas bougé suffisamment pour alimenter le générateur, ou que l'utilisateur soit sur un appareil mobile où il n'y a aucun mouvement de souris. Je recommande donc d'avoir un cas de repli où vous pouvez toujours obtenir un nombre aléatoire non sécurisé si vous n'avez pas le choix. Voici comment j'ai géré cela :

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;
};

Vous devrez inclure sjcl.js et isaac.js pour cette mise en œuvre, et veiller à lancer le collecteur d'entropie sjcl dès que votre page est chargée :

sjcl.random.startCollectors();

sjcl est sous double licence BSD et GPL, tandis qu'isaac.js est sous MIT, il est donc parfaitement possible d'utiliser l'un ou l'autre dans n'importe quel projet. Comme mentionné dans une autre réponse, clipperz est une autre option, mais pour une raison bizarre, il est sous licence AGPL. Je n'ai encore vu personne qui semble comprendre les implications que cela a pour une bibliothèque JavaScript, mais je l'éviterais universellement.

Une façon d'améliorer le code que j'ai posté pourrait être de stocker l'état du générateur de nombres aléatoires isaac dans localStorage, afin qu'il ne soit pas réalimenté à chaque fois que la page est chargée. Isaac génère une séquence aléatoire, mais à des fins de cryptographie, la graine est très importante. L'ensemencement avec Math.random est mauvais, mais au moins un peu moins mauvais si ce n'est pas nécessairement à chaque chargement de page.

22voto

eBusiness Points 2533

Vous pouvez par exemple utiliser le mouvement de la souris comme semence pour les nombres aléatoires, lire l'heure et la position de la souris à chaque fois que l'événement onmousemove se produit, transmettre ces données à une fonction de blanchiment et vous aurez sous la main des nombres aléatoires de première classe. Cependant, assurez-vous que l'utilisateur a suffisamment déplacé la souris avant d'utiliser les données.

Edit : J'ai moi-même joué un peu avec le concept en faisant un générateur de mots de passe, je ne garantirais pas que ma fonction de blanchiment soit sans faille, mais étant constamment réalimenté, je suis presque sûr qu'il fait largement l'affaire : ebusiness.hopto.org/generator.htm

Edit2 : Il fonctionne maintenant en quelque sorte avec les smartphones, mais seulement en désactivant la fonctionnalité tactile pendant la collecte de l'entropie. Android ne fonctionnera pas correctement d'une autre manière.

4voto

ameer Points 937

Vous pouvez essayer http://sourceforge.net/projects/clipperzlib/ Il dispose d'une implémentation de Fortuna qui est un générateur de nombres aléatoires sécurisé sur le plan cryptographique. (Jetez un œil à src/js/Clipperz/Crypto/PRNG.js). Il semble qu'il utilise également la souris comme source d'aléa.

2voto

user2674414 Points 1

Tout d'abord, vous avez besoin d'une source d'entropie. Par exemple, le mouvement de la souris, un mot de passe, ou tout autre. Mais toutes ces sources sont très loin d'être aléatoires, et vous garantissent 20 bits d'entropie, rarement plus. L'étape suivante que vous devez franchir est d'utiliser un mécanisme comme le "KDF basé sur le mot de passe" ; il rendra difficile sur le plan informatique de distinguer les données de l'aléatoire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X