使用JavaScript保护随机数?


78

如何在JavaScript中生成加密安全的随机数?


3
“加密”到底是什么意思?使用Math.random()返回0到1之间的随机数。从技术上讲,它是伪随机数,因为实际上没有任何(简单的)方法可以生成真实的随机数。
逻辑艺术家,2010年

26
我认为问题是需要一个适合加密的随机数生成器。例如。默认情况下,由多种语言实现的模RND将不合适。
Winwaed


18
@Logic艺术家-不,的Math.random是加密的安全。加密安全性是一个标准术语,它意味着该值是无法预测的,即使对于愿意花费大量时间和精力尝试对其进行预测或将其与随机值区分开来的对手也是如此。
DW

Answers:


24

例如,您可以将鼠标移动用作随机数的种子,在onmousemove事件发生时读出时间和鼠标位置,将数据提供给美白功能,您将获得一些一流的随机性。尽管在使用数据之前请确保用户已充分移动鼠标。

编辑:我自己做了一个密码生成器来处理这个概念,我不能保证我的美白功能是完美的,但是不断地植入种子,我很确定这是一件很有意义的事情:ebusiness.hopto.org /generator.htm

Edit2:它现在可以在智能手机上使用,但只能在收集熵时禁用触摸功能。Android无法以其他任何方式正常工作。


11
这是具有BSD许可证和随机数生成器的密码库:crypto.stanford.edu/sjcl
aaaaaaaaaaaaaa

看来确实符合OP的要求。
总统詹姆斯·波尔克(James K. Polk)2010年

SJCL(斯坦福加密图书馆)看起来是一个绝佳的选择。他们发表了一篇论文,详细描述了如何生成密码随机数,并且他们的方法看起来扎实且经过深思熟虑。
DW

我有一个电子商务建议:添加一个分隔符字段,该字段将导致在每个.password span标签之间插入该字符串,从而使复制/粘贴/操作变得容易。例如,当前,如果我复制并粘贴生成的字符串,它们将被粘贴为一个长字符串。
trusktr 2012年

请注意,这在移动平台上不起作用,因为没有鼠标,因此使用此功能的用户将需要后备随机数源。
Matt Eskridge 2014年

60

在WHATWG上已经进行了有关将其添加到window.crypto对象的讨论。您可以阅读讨论内容并查看建议的API和Webkit错误(22049)。

刚刚在Chrome中测试了以下代码以获取随机字节:

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


如果您将替换为window.crypto,则它在IE 11中有效window.msCrypto
Michael Kropat

27

我认为最好的选择是:

  1. window.crypto.getRandomValues或window.msCrypto.getRandomValues
  2. sjcl库的randomWords函数(http://crypto.stanford.edu/sjcl/
  3. isaac库的随机数生成器(由Math.random播种,因此并不是真正的密码安全)(https://github.com/rubycon/isaac.js

window.crypto.getRandomValues已经在Chrome中实现了一段时间,最近在Firefox中也已实现。不幸的是,Internet Explorer 10及更高版本未实现该功能。IE 11具有window.msCrypto,可完成相同的操作。sjcl拥有大量源自鼠标移动的随机数生成器,但始终有可能是鼠标移动不充分,无法生成生成器的种子,或者用户处于移动设备上,而鼠标没有任何移动。因此,我建议您进行后备测试,如果没有选择,您仍然可以得到一个非安全随机数。这是我的处理方式:

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

您需要为该实现包括sjcl.js和isaac.js,并确保在页面加载后立即启动sjcl熵收集器:

sjcl.random.startCollectors();

sjcl是双重许可的BSD和GPL,而isaac.js是MIT,因此在任何项目中使用其中任何一个都是绝对安全的。正如在另一个答案中提到的那样,clipperz是另一种选择,但是出于奇怪的原因,它是根据AGPL许可的。我还没有看到任何人似乎了解JavaScript库的含义,但是我会普遍避免使用它。

改进我发布的代码的一种方法可能是将isaac随机数生成器的状态存储在localStorage中,因此不会在每次加载页面时重新播种它。Isaac将生成一个随机序列,但是出于加密目的,种子非常重要。使用Math.random进行播种是不好的,但如果不一定在每次页面加载时都至少可以减少一点。


我在github.com/simbo1905/srp-6a-demo/blob/master/srp/Client/lib/…采用了这种方法,以创建一个随机的128十六进制数。它的用户window.crypto else isaac。如果必须使用isaac,它将通过跳过随机数0.1s来预热生成器的onpageload。文本输入字段onkeyup也确实random16byteHex.advance(Math.floor(event.keyCode/4));进一步将向前的随机数跳过了几毫秒。这将使该浏览器应用程序中使用的isaac randoms取决于用户输入和硬件/浏览器速度,因此很难猜测。
simbo1905

1
@ZeroG关于您对SJCL的评论:“总是有机会鼠标移动得不足以播种生成器,或者用户正在移动设备上,而鼠标没有任何移动”。现在它在移动设备上运行良好,因为现在从touchmovepull#151)和devicemotionpull#79)收集了熵。
TachyonVortex

1
似乎sjcl已经使用window.crypto可用
Ales

14

使用window.crypto.getRandomValues,如下所示:

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

所有现代浏览器支持此功能并使用操作系统的随机生成器(例如/dev/urandom)。如果需要IE11兼容性,则必须通过使用它们的前缀实现var crypto = window.crypto || window.msCrypto; crypto.getRandomValues(..)

请注意,window.cryptoAPI还可直接生成密钥,这可能是更好的选择。


我认为您的意思是Uint8Array(检查拼写)
Flyingkiwi,2013年

1
“密钥长度”在这里是否正确?密钥长度不是以比特为单位吗?
Indolering '16

1
如果要生成特定范围内的随机数(例如4000-64000),并且每次需要1个随机数,该如何使用window.crypto.getRandomValues。
2017年

2
@Sid听起来是个好问题。问吧
phihag

2
@phihag:已经做了,直到现在还没有答案。stackoverflow.com/questions/41437492/…–
Sid


4

您可能想尝试 http://sourceforge.net/projects/clipperzlib/。 它具有Fortuna的实现,该实现是一种加密安全的随机数生成器。(看看src / js / Clipperz / Crypto / PRNG.js)。似乎也将鼠标用作随机性的来源。


关于图书馆的更多详细信息,请点击这里clipperz.com/open_source/javascript_crypto_library
阿米尔

1
好的答案,很遗憾,它是根据AGPL许可的,我认为与我的项目不兼容。
凯尔(Kyle)2010年

自2014年5月2日起,Clipperz提交了c9f12e87c7ac88e4612de4d1d70df7c53f77e2ad
GGG

1

首先,您需要一个熵源。例如,鼠标,密码或其他任何东西的移动。但是所有这些来源都离随机性很远,并且可以保证20比特的熵,很少。您需要采取的下一步是使用“基于密码的KDF”之类的机制,这将使计算上难以区分数据与随机数据。


0

许多年前,您必须实现自己的随机数生成器,并将其植入通过鼠标移动和计时信息收集的熵中。这就是JavaScript密码学的Phlogiston时代。这些天我们必须window.crypto一起工作。

如果您需要一个随机整数,那么random-number-csprng是一个不错的选择。它安全地生成一系列随机字节,然后将其转换为无偏随机整数。

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

如果您需要一个随机的浮点数,则需要做更多的工作。通常,安全随机性是整数问题,而不是浮点问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.