Math.random()是否具有加密安全性?

62

在不同的浏览器中,JavaScript 中使用的算法,如 Math.random(),有多好?它是否适用于生成盐和一次性密码?

从一个 random 中我可以使用多少位?

7个回答

57

4
作为补充说明,生成盐并不需要使用具有密码学安全性的伪随机数生成器。盐只需要是唯一且是非确定性生成的即可。 - Nick Johnson
不错的观点,但是再次强调,短密码+盐可能会受到彩虹表攻击。在涉及到加密学的情况下,最好使用加密级别的随机数生成器。 - Teoman Soygul
1
@Teoman 盐和密码的长度与盐的来源无关。当然,加密安全通常更可靠。 - Nick Johnson
实际上,它不必很短,因为可预测(如random()),这使得它成为字典/表攻击的非常好的目标。 - Teoman Soygul
@Teoman 这就是我说“确定性”的原因。只有在第一位提供有关后续位的信息以减少熵 - 这只有非常差的 RNG(如 LCG)才会做到 - 或者如果可以从其他信息(如密码)预测盐,才能进行预测。即使是半好的非加密 PRNG 也不符合这两种情况。 - Nick Johnson
显示剩余2条评论

23

这个PRNG(伪随机数生成器)非常不安全,在某些情况下甚至是可预测的,你可以重建PRNG的内部状态,推导出种子,然后可以用它来跟踪人们在网站间的活动,即使他们没有使用cookies,并且隐藏在洋葱路由之后等...

2022编辑:如果你需要JavaScript中的加密RNG,请使用Crypto.getRandomValues


谢谢您提供的链接,这是一个很好的例子,展示了浏览器 PRNG 的弱点。 - Maxim Razin
我想要链接的论文实际上是更通用的 http://www.trusteer.com/files/Temporary_User_Tracking_in_Major_Browsers.pdf。 - Bruno Rohée
找到了第一篇论文的新链接并进行了关联。 - Bruno Rohée

12
截至2013年3月,window.crypto.getRandomValues是一个“实验性技术”,自Chrome 11和Firefox 21以来提供,可让你获取加密随机值。此外,请参阅最新的W3CWeb Cryptography API草案中的getRandomValues描述: 如果您提供基于整数的TypedArray(即Int8ArrayUint8ArrayInt16ArrayUint16ArrayInt32ArrayUint32Array),该函数将使用强密码学随机数生成器填充数组。浏览器应该使用强(伪)随机数生成器。 如果请求的长度大于65536字节,则该方法会抛出QuotaExceededError。 示例:
var array = new Uint32Array(10);
window.crypto.getRandomValues(array);

console.log("Your lucky numbers:");
for (var i = 0; i < array.length; i++) {
    console.log(array[i]);
}

同时,对于JavaScript的Math.random()函数有多随机的回答,请参考此处。此外,来自2008年的一份报告讨论了JavaScript Math.random()函数如何泄漏信息和跨域信息泄露和攻击。

更新:查看Modern.IE Web Crypto API部分可获取当前浏览器支持状态,并且该部分还连接了ChromeFirefoxSafari的错误报告。


Web Crypto API 的当前状态链接可以在此处找到:https://developer.microsoft.com/zh-cn/microsoft-edge/platform/status/webcryptoapi/ - peater

5

由于您无法确切知道浏览器的实现(除了像您业务内部网这样的封闭用户组),因此我通常认为RNG是薄弱的。

即使您可以识别浏览器,您也不知道浏览器本身或任何其他浏览器代理ID是否被篡改。如果可以的话,您应该在服务器上生成数字。

即使您在JavaScript中包含一个良好的PRNG,您的服务器也无法知道客户端的请求是否来自未修改的脚本。如果数字进入您的数据库和/或用作加密工具,则不应完全信任来自客户端的数据。这不仅适用于有效性(您确实验证来自客户端的所有数据吗?),而且还适用于一般属性,如随机性。


0
这是我在Typescript中提出的解决方案:
/**
 * This method returns a random number between 0 and 1 (Compatible with Math.random())
 * // Compliant for security-sensitive use cases
 * @returns Random number between 0 and 1.
 */
export const randomNumber = (): number => {
  // RandomBytes generates 4 random bytes, which are then read as a 32-bit unsigned integer in little endian.
  // The max value of a 32bit unsigned int is 0xFFFFFFFF (or 4,294,967,295 in decimal).
  // By dividing the randomly generated 32bit int by its max value you get a value between 0 and 1.
  // eslint-disable-next-line unicorn/number-literal-case

  return randomBytes(4).readUInt32LE(0) / 0xffffffff;
};

-1
如果你正在寻找一个定制的随机数生成器,而且还要通过密码学检查。下面这样简单的东西就足够了。
function numberGenerator(max){
    let num = (Date.now() +''); 
    num = parseInt(num.at(num.length-1));

    while(num > max){
        num -= max        
    }

    return num;
}

这个函数可以返回一个介于0-9之间的随机数,该随机数小于你在参数中提供的最大值。你可以随意修改它,或者调用两次以获取两位数并将它们连接起来。

-2

Math.random() 不具备加密安全性。此外,Veracode将用以下方式指出此情况

CWE-331(不足的熵)

我们可以使用SecureRandom来实现类似的功能。

new SecureRandom().nextDouble();

6
JavaScript中是否有SecureRandom()方法可用? - Krishna Pandey
3
“SecureRandom.nextDouble” 似乎是 Java 中的函数,对于我们在 JavaScript 中来说毫无用处。 - RashadRivera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接