【问题标题】:Is it okay to utilize Math.random() to generate a Password-Reset-Key?可以使用 Math.random() 生成密码重置密钥吗?
【发布时间】:2018-07-28 17:01:16
【问题描述】:

我对一个简单的算法有一个非常基本的想法,它允许用户通过请求发送到他们的电子邮件的随机代码来重置他们的密码。

如果用户忘记了他们的 PW,他们可以请求服务器生成重置代码并将其作为电子邮件发送给他们。然后将此代码附加到他们在数据库中的用户条目中。确认输入表单将要求用户在前端输入重置代码 - 如果失败,代码将被标记并失效。如果成功,他们将被发送到一个新的 PW 重置表单,在那里他们可以通过向后端发送 PUT 请求来更新要替换的凭据。

这是我的基本想法 -

function generate7KeyPasswordResetConfirmationCode() {

    const arrayOfSeven = [1, 2, 3, 4, 5, 6, 7]

    const alphabet = [Array of 26 Lowercase Chars and 26 Uppercase Chars]

    const letterOrNumber = arrayOfSeven.map(currentPos => {
        // return pseudorandom number between 0-100
        const oneToOneHundred = Math.floor(Math.random() * Math.floor(101))
            if (oneToOneHundred < 50) { return "char" }
            else if (oneToOneHundred >= 50) { return "num" }        
    })

    const resetKey = letterOrNumber.map(numOrChar => {
        // return pseudorandom number between 0-51 and return a-z-A-Z
        if (numOrChar === "char") {
            const index = Math.floor(Math.random() * Math.floor(52))                
              return alphabet[index]
        }
        // return pseudorandom number 0-9
        if(numOrChar === "num") { 
            return Math.floor(Math.random() * Math.floor(10)) 
        }
    })

        return resetKey
}

但是,我的方法涉及到 Math.random(),我在 MDN 上注意到了这个东西:

Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method

我意识到我可以只使用 uuid4,但我想创建自己的解决方案 - 这究竟是如何破解的?如果我在第一次尝试失败时使代码无效,还会出现什么问题?

【问题讨论】:

  • 什么意思?你能详细说明一下吗?
  • 使用随机数生成密码与使用它们进行密码学完全不同。
  • 一般的想法是创建一个长度为 7 的随机字符串 - 使用 math.rand() 两次,所以有两件事是随机的 - 给定的索引是数字还是字母 - 并且实际数字或字母。这只是一种允许用户更改其 PW 的简单方法 - 实际的 PW 加密是在后端使用 bcrypt 完成的
  • @MaartenBodewes,我看到了这个 - 但是文档并不足以让我理解它。返回的数字范围是多少?它似乎在负150和正150之间?我需要能够控制但随机化数字范围。无论如何,我尝试了我的解决方案,它形成了一个随机的 7 个字符键,字母/数字位置是随机的。通过一些进一步的加密层,我认为这是一个足够的解决方案。

标签: javascript node.js authentication cryptography authorization


【解决方案1】:

不,使用不安全的随机数生成器绝不是个好主意。随机生成的密码应具有与随机生成的密钥相似的属性。无法保证随机数生成器不受其他脚本的影响。在最坏的情况下,其他脚本可以找出随机数生成器的状态并直接计算您的密码。

所以请使用window.crypto.getRandomValues(array) 生成密码。可以在here 找到从 secure 随机数生成器中检索密码的无偏见实现。


如果您无权访问随机数生成器,则:

  1. 从服务器获取安全随机数;
  2. 添加来自客户端的随机熵(鼠标移动、系统时间、一些不安全的随机值等)并将它们连接到来自服务器的随机;
  3. 对结果数组执行安全计算,例如哈希或 PRNG 实施,以创建随机数;
  4. 使用 (3) 创建密码。

您绝对应该只通过安全连接执行此操作,并谨记网络钓鱼攻击。

【讨论】:

  • 感谢您提供信息丰富的答案 - 如何处理不支持加密对象的浏览器?在你朋友的代码中,他只是抛出了一个错误。我想这是我们能做的最好的 - 拒绝重置使用旧浏览器的人的密码,因为它可能是攻击者利用系统?
  • 我想我试图在第二部分中介绍这一点。通常,您会尝试将来自服务器的强随机与来自客户端的较弱随机混合。这样,密码不仅取决于服务器,而且其他脚本等仍然无法在客户端上猜到随机密码。我同意,这不是那么简单,但安全性通常不是。但请注意,目前所有桌面浏览器似乎都支持该对象。更棘手的是客户端禁用 JavaScript。