在Javascript中生成加密强的伪随机数?

信息安全 密码学 Web应用程序 javascript 随机的 密钥生成
2021-08-19 13:01:23

有没有什么好的方法可以在 Javascript 中生成加密强的伪随机(或真正的随机)数?

关键要求:如果 a.com 的 Javascript 生成一些随机数,其他人应该无法预测这些随机数。例如,evil.com 上的 Javascript 应该无法预测 a.com 获得的随机数。

总结我对这个主题的了解。 这是我在自己的研究中能够找到的:

  • 所有浏览器都提供Math.random()作为库调用来生成伪随机数。不幸的是,它不提供加密质量的随机数并且不符合上述要求。在我见过的浏览器上,Math.random()使用非加密 PRNG,其内部状态在多个站点之间共享。所以 evil.com 可以调用Math.random()一堆,恢复 PRNG 的内部状态,然后推断 a.com 调用时得到了哪些随机数Math.random()此外,它使用非加密质量的 PRNG,因此如果 a.com 使用Math.random()然后发布 IV 生成随机密钥和随机 IV,则有可能推断 PRNG 的内部状态(从 IV)然后恢复钥匙。所以,Math.random()就出来了。

  • 我发现一篇研究论文着眼于用 Javascript 进行密码学。他们的代码,Stanford Javascript Crypto Library,是公开的。它确实包括一个加密强度的伪随机数。

    但是,它似乎有一个很大的限制:如果我正确地阅读了论文,那么在伪随机数得到充分播种之前,用户与您的站点进行交互需要 10-40 秒。此外,每个站点都必须从头开始:如果 a.com 包含 SJCL 库,那么看起来 a.com 的脚本必须等待用户与 a.com 站点交互 10-40 秒(通常)在 a.com 可以生成加密质量的随机数之前。这是一个非常重要的限制。

    这是他们的论文:

  • 经典文章Javascript Cryptography Considered Harmful提到,缺乏任何在 Javascript 中获取加密强度随机数的好方法是在 Javascript 中进行安全加密的主要障碍。本文考虑了几种明显的方法,并解释了它们存在缺陷的原因。

底线是我不知道任何合理的解决方案;我发现的选项似乎都很有问题。然而,这些论文和论文写完已经好几年了,我知道网络技术可以迅速改变。有谁知道这个问题的任何好的解决方案?

4个回答

有一个实验性的 API:window.crypto.getRandomValues

它支持在

  • 铬 11.0
  • 火狐 21
  • 互联网探索 11.0
  • 带有 Opera 皮肤 15.0 的 Chrome

Opera 12 不支持此 API,但它Math.Random是安全的。

Opera 还没有实现 window.crypto.getRandomValues()。但是,我们的 Math.Random() 使用的是加密安全的随机生成器。如果小心使用(注意它每次调用只返回 53 位熵),可以使用它来实现一个 javascript 版本的 window.crypto.getRandomValues()。只需确保仅在 Opera 中执行此操作,并在代码中添加一些好的注释即可。

http://lists.w3.org/Archives/Public/public-webcrypto/2013Jan/0063.html

在 Web 上下文中,Javascript 由受信任的 Web 服务器提供(即,通过发送更改的代码,已经有能力变得非常讨厌的 Web 服务器)。您最好继续信任它,并通过辅助 Ajax 调用从该服务器请求随机种子,或者只是让服务器直接在 Javascript 代码中包含随机种子,这应该是几行的问题PHP 代码。像这样的东西:

<script src="theJavascriptCode.js"></script>
<?php
    $randblob = bin2hex(openssl_random_pseudo_bytes(16));
    echo "<script>init_PRNG(\"$randblob\")</script>"
?>

然后,Javascript 代码可以包含一个良好的 PRNG 实现,通过该init_PRNG()函数播种。

(不过,针对草率的集成商的安全性可能很困难。您无法仅从 Javascript 轻松测试它的种子确实是由强大的 PRNG 提供的,而不是来自弱的东西,甚至是硬编码的服务器端。)

您还可以尝试Javascript 加密库(AGPL 3 许可证)中包含的 Fortuna 实现

(由在线密码管理器Clipperz制作,我是联合创始人)

不幸的是,浏览器只是没有提供足够的熵来产生强随机数。我认为它总是会在可用性方面做出妥协。

我会去看看seedrandom.js(BSD 许可证)。它基于 RC4,似乎很受欢迎。它允许您使用相当简单的 API 从自己的来源插入熵。我只使用过一次,而且我从未真正研究过安全证明,但它肯定比我见过的许多其他解决方案更容易和更快。

我的额外熵源是:

  • PHP的哈希mt_rand()microtime()输出
  • 每 1 秒获取一次鼠标位置数据
  • 键盘计时数据(在这种情况下,用户在需要 RNG 之前填写了表格)

您还可以对网络上的几个 Ajax 调用进行计时并将这些计时添加到池中。

我不知道真正产生了多少熵,但我希望在不给用户带来不便的情况下尽可能多地获取。