在javascript中保护随机数?

IT技术 javascript cryptography
2021-02-05 13:55:12

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

6个回答

WHATWG 已经讨论过将其添加到 window.crypto 对象。您可以阅读讨论并查看提议的 API和 webkit 错误 (22049)。

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

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

如果您替换window.cryptowindow.msCrypto.
2021-04-11 13:55:12

按顺序,我认为您最好的选择是:

  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 其他 isaac。如果必须使用 isaac,它将通过跳过 0.1 秒的随机数来预热生成器 onpageload。文本输入字段 onkeyup 也random16byteHex.advance(Math.floor(event.keyCode/4));可以进一步向前跳过一些毫秒的随机数。这将使该浏览器应用程序中使用的 isaac 随机数取决于用户输入和硬件/浏览器速度,因此很难猜测。
2021-03-17 13:55:12
看来sjcl已经使用 window.crypto 可用
2021-03-22 13:55:12
@ZeroG 关于您对 SJCL 的评论:“总是有可能鼠标没有充分移动来为生成器提供种子,或者用户使用的移动设备上没有任何鼠标移动”它现在在移动设备上运行良好,因为现在从touchmove( pull #151 ) 和devicemotion( pull #79 )收集熵
2021-03-24 13:55:12

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

编辑:我通过制作密码生成器对这个概念进行了一些尝试,我不能保证我的美白功能是完美无缺的,但是不断重新播种我很确定它足以完成这项工作:ebusiness.hopto.org /generator.htm

Edit2:它现在有点适用于智能手机,但只能在收集熵时禁用触摸功能。Android 不会以任何其他方式正常工作。

我有一个建议电子商务:添加一个分隔符字段,它会导致在每个.password span标签之间插入该字符串以使复制/粘贴/操作变得容易。例如,目前,如果我复制并粘贴生成的字符串,它们将被粘贴为一个长字符串。
2021-03-16 13:55:12
这是一个带有 BSD 许可证和随机数生成器的加密库:crypto.stanford.edu/sjcl
2021-03-24 13:55:12
SJCL(斯坦福加密图书馆)看起来是个不错的选择。他们发表了一篇论文,详细描述了他们如何生成加密随机数,他们的方法看起来很可靠且经过深思熟虑。
2021-03-24 13:55:12
这似乎确实满足 OP 的要求。
2021-04-03 13:55:12
请注意,这在移动平台上不起作用,因为没有鼠标,因此使用此功能的人将需要回退随机数源。
2021-04-03 13:55:12

使用window.crypto.getRandomValues,像这样:

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

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

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

@phihag:已经做了,直到现在还没有答案。stackoverflow.com/questions/41437492/...
2021-03-16 13:55:12
“密钥长度”在这里是正确的术语吗?密钥长度不是以比特为单位吗?
2021-03-20 13:55:12
@Sid 这听起来是一个很好的问题。问吧
2021-03-26 13:55:12
如果我想生成特定范围内的随机数,比如 4000-64000,并且每次我需要 1 个随机数,那么如何使用 window.crypto.getRandomValues。
2021-03-27 13:55:12
我想你的意思是 Uint8Array (检查拼写)
2021-04-03 13:55:12

要从范围[0, 1)(类似于Math.random())中获取加密强数,请使用crypto

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

console.log( random() );