【问题标题】:User verification on the reset password page重置密码页面上的用户验证
【发布时间】:2011-01-03 05:08:20
【问题描述】:

我正在为我的网站编写密码重置页面。这是我的想法:

一个。用户点击登录页面上的“忘记密码”链接

b.重定向到我的密码重置页面

c。用户输入他的电子邮件地址

d。一封电子邮件发送到电子邮件地址,其中包含重置密码的链接。该链接中包含安全代码,例如 ?code="xxxx"。

e。用户打开链接并输入新密码,然后点击提交按钮。

f。我的页面更改了用户的密码。

我的问题是关于步骤 f。在步骤 e 中,当用户打开链接时,我可以验证他的安全码,然后向用户显示“新密码”和“确认密码”字段。但是当用户点击提交按钮时,我怎么知道这是用户而不是黑客提交的真实请求呢?也许我错了,但我认为黑客可以很容易地模拟这样的字段数据,因为没有验证字段。

我可以想到一些想法来验证步骤 f 中的请求,但我不知道它们是否正确。 1、在步骤e中添加一个加密的cookie并在步骤f中检查? 2.在步骤e中使用会话变量并在步骤f中检查它? 3.在步骤e中添加隐藏字段,在步骤f中检查?

这些方法可以吗?哪个更好,或者有没有更好的?

提前致谢。

【问题讨论】:

    标签: asp.net forgot-password


    【解决方案1】:

    输入用户名和重置代码的用户应该像用户名和密码一样登录网站。不同之处在于您会立即强制他们更改密码。使用这种密码重置方法,您隐含地相信用户是发送代码的电子邮件帐户的所有者。

    编辑:

    好的,所以我不知道关于 ASP.net 的第一件事。

    但是,我之前已经多次处理过这个问题。这是我在 PHP 中的一个解决方案:

    <?php
    class AuthController extends Zend_Controller_Action
    {
        public function identifyAction()
        {
            if ($this->_request->isPost()) {
                $username = $this->_getParam('username');
                $password = $this->_getParam('password');
    
                if (empty($username) || empty($password)) {
                    $this->_flashError('Username or password cannot be blank.');
                } else {
                    $user = new User();
                    $result = $user->login($username, $password);
    
                    if ($result->isValid()) {
                        $user->fromArray((array) $this->_auth->getIdentity());
    
                        if ($this->_getParam('changepass') || $user->is_password_expired) {
                            $this->_redirect('auth/change-password');
                            return;
                        }
                        $this->_doRedirect($user);
                        return;
                    } else {
                        $this->_doFailure($result->getIdentity());
                    }
                }
            }
            $this->_redirect('/');
        }
    
        public function forgotPasswordAction()
        {
            if ($this->_request->isPost()) {
                // Pseudo-random uppercase 6 digit hex value
                $resetCode = strtoupper(substr(sha1(uniqid(rand(),true)),0,6));
    
                Doctrine_Query::create()
                    ->update('dUser u')
                    ->set('u.reset_code', '?', array($resetCode))
                    ->where('u.username = ?', array($this->_getParam('username')))
                    ->execute();
    
                $mail = new Zend_Mail();
                $mail->setBodyText($this->_resetEmailBody($this->_getParam('username'), $resetCode));
                $mail->setFrom('no-reply@example.com', 'Example');
                $mail->addTo($this->_getParam('username'));
                $mail->setSubject('Forgotten Password Request');
                $mail->send();
    
    
                $this->_flashNotice("Password reset request received.");
                $this->_flashNotice("An email with further instructions, including your <em>Reset Code</em>, has been sent to {$this->_getParam('username')}.");
                $this->_redirect("auth/reset-password/username/{$this->_getParam('username')}");
            }
        }
    
        public function resetPasswordAction()
        {
            $this->view->username = $this->_getParam('username');
            $this->view->reset_code = $this->_getParam('reset_code');
    
            if ($this->_request->isPost()) {
                $formData = $this->_request->getParams();
                if (empty($formData['username']) || empty($formData['reset_code'])) {
                    $this->_flashError('Username or reset code cannot be blank.');
                    $this->_redirect('auth/reset-password');
                } elseif ($formData['new_password'] !== $formData['confirm_password']) {
                    $this->_flashError('Password and confirmation do not match.');
                    $this->_redirect('auth/reset-password');
                } else {
                    $user = new User();
                    $result = $user->loginWithResetCode($formData['username'], $formData['reset_code']);
    
                    if ($result->isValid()) {
                        $user->updatePassword($result->getIdentity(), $formData['new_password']);
    
                        $user->fromArray((array) $this->_auth->getIdentity());
                        $this->_setLegacySessionData($user);
    
                        $this->_flashNotice('Password updated successfully!');
                        $this->_doRedirect($user);
                    } else {
                        $this->_doFailure($result->getIdentity());
                        $this->_redirect('auth/reset-password');
                    }
                }
            }
        }
    
        protected function _doFailure($username)
        {
            $user = Query::create()
                ->from('User u')
                ->select('u.is_locked')
                ->where('u.username = ?', array($username))
                ->fetchOne();
    
            if ($user->is_locked) {
                $lockedMessage = Config::get('auth.lock_message');
                if (!$lockedMessage) {
                    $lockedMessage = 'This account has been locked.';
                }
                $this->_flashError($lockedMessage);
            } else {
                $this->_flashError('Invalid username or password');
            }
        }
    }
    

    如果你能遵循这个,它应该让你知道该怎么做。我试着总结一下:

    识别动作

    这是使用用户名和密码的常规“登录”。它将用户登录并将其身份存储在会话中。

    忘记密码动作

    这会向用户显示一个请求其用户名的表单。输入用户名后,会生成一个重置代码,将其存储在用户表的条目中,然后通过电子邮件发送并重定向到重置密码页面。此页面未经身份验证,用户未登录。

    重置密码动作

    这是向用户显示“resetPassword”表单的地方。他们必须提供他们的用户名和通过电子邮件收到的重置代码。这验证用户使用给定的用户名和重置代码,就像重置代码是密码一样。如果凭据有效,则用户将被重定向到 changePassword 操作,允许他们更改密码。 changePasswordAction(未显示)要求用户通过用户名/密码或用户名/resetCode 进行身份验证(登录)

    希望这会有所帮助。

    【讨论】:

    • 但是我检查了serverl站点,发现他们在恢复密码时不会登录用户。两个密码字段仅用于重置密码,不用于更改密码。
    • 我看不出您的评论与我的回答有什么相关性。
    • 我的意思是在几个网站上,用户输入他们的用户名和重置代码不会登录。它只是将用户引导到一个页面来更改他们的密码,但他们没有登录。跨度>
    • hobodave,感谢您的代码和总结。他们有很大帮助。但我认为这些代码在恢复密码时仍会记录用户。为什么不呢,我会尽量接近。谢谢。
    【解决方案2】:

    如果您通过电子邮件发送的代码是 GUID 或某个此类 ID,那么从统计数据来看,有人能够猜出该代码的可能性很小。如果您还让链接包含他们电子邮件的散列版本或将代码链接到用户的其他方式,我认为您会非常安全地免受恶意输入。

    我更担心人们会从步骤 c/d 收到垃圾邮件,除非您正在对数据库中当前存在的电子邮件进行某种验证。

    【讨论】:

      猜你喜欢
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 2012-09-20
      • 2022-07-06
      • 2019-07-02
      • 1970-01-01
      相关资源
      最近更新 更多