关于asp.net:重置密码页面上的用户验证

User verification on the reset password page

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

a.用户点击登录页面上的"忘记密码"链接

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

C.用户输入其电子邮件地址

d.发送到电子邮件地址的电子邮件,带有重置其密码的链接。链接有类似的安全代码?其中code="XXXX"。

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

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

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

我可以考虑在步骤f中验证请求,但我不知道它们是否正确。1。在步骤e中添加加密cookie并在步骤f中检查它?2。使用步骤e中的会话变量并在步骤f中检查它?三。在步骤e中添加隐藏字段并在步骤f中检查它?

这些方法可以吗?哪一个更好,还是有更好的?

事先谢谢。


输入用户名和重置代码的用户应该像输入用户名和密码一样将其登录到站点。不同的是,你会立即强制他们更改密码。使用此密码重置方法,您将隐式信任用户是发送代码的电子邮件帐户的所有者。

编辑:

好吧,所以我不知道ASP.NET的第一件事。

但是,我以前处理过很多次这个问题。下面是我在PHP中的一个解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?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('[email protected]', '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 Reset Code, 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');
        }
    }
}

如果你能做到这一点,它应该能让你对该做什么有一个很好的了解。我试着总结一下:

确定行动

这是使用用户名和密码的常规"登录"。它将用户登录并将其标识存储在会话中。

ForgotPasswordAction(ForgotPasswordAction)

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

重置密码操作

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

希望这有帮助。


使用忘记密码标签搜索stackoverflow上的其他问题。关于好的算法和技术,已经有好几个书面的答案可以使用。


如果你发邮件的代码是一个guid或者类似的id,那么从统计上来说,有人猜测代码的可能性很低。如果您另外拥有链接,包括他们电子邮件的散列版本或其他将代码链接到用户的方式,我认为您可以很好地避免恶意输入。

我会更担心人们被从C/D步骤中删除,除非你正在对数据库中现有的电子邮件进行某种验证。