【问题标题】:Secure way to store pending user's password in Meteor在 Meteor 中存储待处理用户密码的安全方法
【发布时间】:2015-05-07 11:07:04
【问题描述】:

对于我的 Meteor 应用程序,我希望有以下注册流程:

  1. 用户注册用户名、电子邮件和密码(他还不能登录。)
  2. 已发送确认电子邮件 [Accounts.sendEnrollmentEmail]
  3. 用户确认电子邮件 [Accounts.onEnrollmentLink]
  4. 用户已创建。 [Accounts.createUser] (他可以登录了。)

为了实现这一点,我觉得我必须将 纯文本 密码存储在临时用户表中(第 1 步),以便稍后创建实际用户(第 3 步) .显然这是一个可怕的想法。

我当然只能从第 3 步开始询问密码并立即创建用户 - 但这不是我想要实现的行为。

那么:是否有适当的方法来安全地存储密码以便以后将其传递给用户创建?或者有没有办法创建一个不可登录的用户?

【问题讨论】:

  • 是否有中间立场——比如允许用户登录,但如果他/她没有任何经过验证的电子邮件,则自动将他们重定向到“您需要验证您的电子邮件”路线?
  • 您可以像创建用户时一样存储散列密码并将其存储在临时用户表中。
  • 同意大卫,常见的模式是允许登录但禁用操作。再说一次,加盐和散列是每个 Web 开发人员都应该解决的一个很好的练习!
  • 顺便说一句,您是否不希望用户在确认电子邮件之前出现在用户集合中?您可以让流星帐户照常工作,只需设置一个 cron 作业即可在 X 小时/天后删除用户而无需确认。
  • 我明白你的意思。您可以散列密码(帐户密码使用 sha256)将其存储在临时表中,并在常规用户创建期间绕过服务器散列密码,如果您给它一个像 {algorithm: "sha-256", digest: "yourhash"} 这样的对象,请检查 getPasswordStringcreateUser功能。

标签: meteor passwords


【解决方案1】:

当您使用内置方法创建用户时,Meteor 提供了保存密码存储所需的一切,您自己无需做太多事情。所以你应该从一开始就使用这些方法(你的第 1 步:Accounts.createUser,第 2 步:Accounts.sendVerificationEmail,第 3 步:Accounts.verifyEmail,不再需要第 4 步了)。

现在要到达您想要的位置,您可以使用 David Weldon 建议的方法,但在服务器端使用 Accounts.validateLoginAttempt。这更容易一些,而且一开始就不允许登录。

例如你可以有这个代码服务器端:

Accounts.validateLoginAttempt(function(loginAttempt){
    if (!loginAttempt.allowed) {
        // Only tell the user that something went wrong but not what to enhance security
        throw new Meteor.Error(901, 'Your login credentials are wrong. Try again.');
    } else {

        // In some cases this method isn't invoked with a correct user object...
        if (!loginAttempt.user) {
            throw new Meteor.Error(902, 'No valid user object. Make sure to validate your email address first.');
        }

        // If email verification is required check if the user has a valid email address and don't allow the login if he has none
        if (!loginAttempt.user.emails[0].verified) {
            throw new Meteor.Error(902, 'Your email address has to be verified first.');
        }

        // We have a correct login!
        return true;
    }
});

现在在客户端,您可以使用这样的逻辑进行登录

Meteor.loginWithPassword(email, password, function(callback) {
    if (callback === undefined) { 
        // Your login logic
    } else if (callback.error == 902) {
        // Your "not verfied" logic
    } else {
        // Your other login errors logic
    }
}

请注意,您可能还需要稍微调整注册过程,因为 Meteor 默认尝试在注册后直接登录用户,但这不再可能了。 另请注意,您可以使用Accounts.validateLoginAttempt 来做更多的事情。例如,您还可以在此处实现一个逻辑,只允许来自同一 IP 的一定数量的错误登录尝试。

【讨论】:

    【解决方案2】:

    根据帐户包,我们在应用中使用了稍微不同的模式

    1. 用户注册
    2. 用户正常登录

    输出主站点模板条件内容与

    <template name="main">
      {{#if currentUser}}
        {{#if verified }}
          ...
        {{else}}
          Notice to user to look for their verification email
        {{/if}}
      {{/if}}
    </template>
    

    基于辅助函数

     Template.main.helpers({
       verified: function() { return Meteor.user().emails[0].verified; }
     });
    

    这满足了用户在经过验证之前不能做太多事情的要求,但仍以简单安全的方式使用帐户包。

    我也可以看到使用 Iron:router 的相关方法。

    【讨论】:

    • 我喜欢这种方法,但不幸的是它不会阻止用户调用方法或从命令行读取订阅集合。因此,有必要为所有方法/订阅/...添加大量逻辑...
    • 这是一个有点老套的方法 - 使用正常流程,但是当他们注册时,在服务器端将 user.services.password.bcrypt 键重命名为 user.services.password.pending (然后将它们注销),然后在电子邮件验证后将其换回。
    • 由于这个问题,我进入了我们的应用程序,并为每个集合添加了 3 个简单、相同的拒绝规则,以防止未经验证的用户进行任何写入。在我的情况下,订阅已经受到待定用户的严格限制。
    猜你喜欢
    • 1970-01-01
    • 2011-02-03
    • 2016-04-22
    • 1970-01-01
    • 2013-01-18
    • 2010-09-16
    • 1970-01-01
    • 2010-12-07
    • 1970-01-01
    相关资源
    最近更新 更多