Проверка пользователя на странице сброса пароля

Я пишу страницу сброса пароля для своего веб-сайта. Вот моя идея:

а. Пользователь нажимает ссылку «забыл пароль» на странице входа.

б. Перенаправить на мою страницу сброса пароля

c. Пользователь вводит свой адрес электронной почты

d. Электронное сообщение, отправленное на адрес электронной почты, со ссылкой для сброса его / ее пароля. В ссылке есть защитный код вида? Code = "xxxx".

е. Пользователь открывает ссылку и вводит новый пароль, а затем нажимает кнопку отправки.

f. Моя страница меняет пароль пользователя.

Мой вопрос касается шага f. На шаге e, когда пользователь открывал ссылку, я мог проверить его код безопасности, а затем показать пользователю поля «новый пароль» и «подтвердить пароль». Но когда пользователь нажал кнопку отправки, как я мог узнать, что это настоящий запрос, отправленный пользователем, а не хакером? Возможно, я ошибаюсь, но я думаю, что хакер может легко смоделировать такие полевые данные, так как нет полей валидации.

Есть некоторые идеи, которые я могу придумать для проверки запроса на шаге f, но я не знаю, верны ли они. 1. Добавить зашифрованный файл cookie на шаге e и проверить его на шаге f? 2. Использовать переменную сеанса на шаге e и проверить ее на шаге f? 3. Добавьте скрытое поле на шаге e и проверьте его на шаге f?

Эти подходы приемлемы? Какой лучше, или есть лучше?

Заранее спасибо.


person wenqiang    schedule 09.01.2010    source источник


Ответы (2)


Пользователь, вводящий свое имя пользователя и код сброса, должен зарегистрировать их на сайте так же, как их имя пользователя и пароль. Разница в том, что вы сразу же заставляете их сменить пароль. Используя этот метод сброса пароля, вы неявно доверяете тому, что пользователь является владельцем учетной записи электронной почты, на которую был отправлен код.

Редактировать:

Хорошо, я ничего не знаю об 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('[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 <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');
        }
    }
}

Если вы можете следовать этому правилу, это должно дать вам хорошее представление о том, что делать. Попробую подвести итог:

identifyAction

Это обычный «вход» с использованием имени пользователя и пароля. Он регистрирует пользователя и сохраняет его личность в сеансе.

forgotPasswordAction

Это представляет пользователю форму, запрашивающую его имя пользователя. После ввода их имени пользователя генерируется код сброса, который сохраняется в их записи в таблице пользователей, и они отправляются по электронной почте, а также перенаправляются на страницу сброса пароля. Эта страница не аутентифицирована, пользователь не вошел в систему.

resetPasswordAction

Здесь пользователю предоставляется форма «resetPassword». Они должны указать свое имя пользователя и код сброса, который они получили по электронной почте. Это аутентифицирует пользователя с указанным именем пользователя и кодом сброса, как если бы код сброса был паролем. Если учетные данные действительны, пользователь перенаправляется к действию changePassword, где ему разрешается изменить свой пароль. Для changePasswordAction (не показано) требуется, чтобы пользователь прошел аутентификацию (вошел в систему) либо с помощью имени пользователя / пароля, либо имени пользователя / resetCode.

Надеюсь это поможет.

person hobodave    schedule 09.01.2010
comment
Но я проверил сайты serverl и обнаружил, что они не регистрируют пользователей при восстановлении их паролей. Два поля пароля предназначены только для сброса пароля, но не для изменения пароля. - person wenqiang; 09.01.2010
comment
Я не вижу отношения вашего комментария к моему ответу. - person hobodave; 09.01.2010
comment
Я имею в виду, что на некоторых веб-сайтах пользователи, вводящие свое имя пользователя и код сброса, не будут входить в систему. Он просто направляет пользователей на страницу для изменения их паролей, но они не вошли в систему. - person wenqiang; 09.01.2010
comment
hobodave, спасибо за коды и резюмируйте. Они очень помогают. Но я думаю, что коды по-прежнему регистрируют пользователей при восстановлении их паролей. Почему бы и нет, попробую подойти. Спасибо. - person wenqiang; 09.01.2010

Если ваш код, который вы отправляете по электронной почте, представляет собой GUID или какой-либо другой идентификатор, существует статистически низкая вероятность того, что кто-то сможет угадать этот код. Если бы у вас дополнительно была ссылка, включающая хешированную версию их электронной почты или какой-либо другой способ привязать код к пользователю, я думаю, вы были бы довольно защищены от злонамеренного ввода.

Я бы больше беспокоился о том, что людей рассылают спамом с шага c / d, если вы не выполняете какую-либо проверку электронной почты, существующей в настоящее время в вашей базе данных.

person Myles    schedule 09.01.2010