1 user_password_reset.test | public UserPasswordResetTest::testResetImpersonation() |
Make sure that users cannot forge password reset URLs of other users.
File
- core/
modules/ user/ tests/ user_password_reset.test, line 175 - Tests for resetting the password.
Class
Code
public function testResetImpersonation() {
// Make sure user 1 has a valid password, so it does not interfere with the
// test user accounts that are created below.
$account = user_load(1);
$account->pass = user_password();
$account->save();
// Create two user accounts with empty passwords. The only way to have an
// empty password is to force it in the database, so do that and reload the
// accounts afterwards.
$user1 = $this->backdropCreateUser();
$user2 = $this->backdropCreateUser();
db_update('users')
->fields(array('pass' => ''))
->condition('uid', array($user1->uid, $user2->uid), 'IN')
->execute();
$user1 = user_load($user1->uid);
$user2 = user_load($user2->uid);
// The password reset URL must not be valid for the second user when only
// the user ID is changed in the URL.
$reset_url = user_pass_reset_url($user1);
$attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
$this->backdropGet($attack_reset_url);
$this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
$this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
// When legacy code calls user_pass_rehash() without providing the $uid
// parameter, neither password reset URL should be valid since it is
// impossible for the system to determine which user account the token was
// intended for.
$timestamp = REQUEST_TIME;
// Pass an explicit NULL for the $uid parameter of user_pass_rehash()
// rather than not passing it at all, to avoid triggering PHP warnings in
// the test.
$reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
$reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
$this->backdropGet($reset_url);
$this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
$this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
$attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
$this->backdropGet($attack_reset_url);
$this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
$this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
// To verify that user_pass_rehash() never returns a valid result in the
// above situation (even if legacy code also called it to attempt to
// validate the token, rather than just to generate the URL), check that a
// second call with the same parameters produces a different result.
$new_reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
$this->assertNotEqual($reset_url_token, $new_reset_url_token);
// However, when the duplicate account is removed, the password reset URL
// should be valid.
user_delete($user2->uid);
$reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
$reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
$this->backdropGet($reset_url);
$this->assertUrl($reset_url, array(), 'The user remains on the password reset login page.');
$this->assertNoText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
}