Weakness reference
CWE-620

Unverified Password Change

This weakness occurs when a system allows a user's password to be changed without confirming the user's identity first — typically by requiring the current…

01Summary

This weakness occurs when a system allows a user's password to be changed without confirming the user's identity first — typically by requiring the current password or sending a verification link to a registered email address. An attacker who gains temporary access to a user's session, browser, or device can permanently lock them out by changing their password without proof of ownership.

02How It Happens

Password change functionality is often treated as a low-risk operation because it seems to occur "within" an authenticated session. However, session hijacking, cross-site request forgery (CSRF), or physical access to an unlocked device can all allow an attacker to reach the password-change form. If the application does not re-authenticate the user — by asking for their current password, a one-time code, or a confirmation email — the attacker can set a new password and retain access while the legitimate owner is locked out.

The root cause is a gap between the authentication check (which verified the user at login) and the authorization check (which should verify they still have the right to change *this specific credential*). Developers sometimes skip the re-authentication step assuming the session itself is proof enough, but sessions can be compromised without the user's knowledge.

03Real-World Impact

An attacker with temporary access to a user's account can change the password and take permanent control, locking out the legitimate owner. This is particularly damaging in shared environments (libraries, offices, schools) or after a phishing attack that tricks a user into logging in on an attacker's device. The victim loses access to their account and all associated data, and recovery may be slow or impossible if the attacker also changes the recovery email or phone number.

04Vulnerable & Fixed Patterns

Vulnerable pattern
@app.route('/change-password', methods=['POST'])
@login_required
def change_password():
    new_password = request.form.get('new_password')
    user = current_user
    user.password_hash = hash_password(new_password)
    db.session.commit()
    return redirect('/dashboard')

Why it's vulnerable:
The function checks that a user is logged in (@login_required) but does not verify their identity by asking for the current password. An attacker with access to the user's session can change the password without knowing the original one.

Fixed pattern
@app.route('/change-password', methods=['POST'])
@login_required
def change_password():
    current_password = request.form.get('current_password')
    new_password = request.form.get('new_password')
    user = current_user
    
    if not verify_password(current_password, user.password_hash):
        return render_template('change_password.html', error='Current password is incorrect')
    
    user.password_hash = hash_password(new_password)
    db.session.commit()
    return redirect('/dashboard')
Vulnerable pattern
<?php
if ( is_user_logged_in() ) {
    if ( isset( $_POST['new_password'] ) ) {
        $user_id = get_current_user_id();
        $new_password = $_POST['new_password'];
        wp_set_password( $new_password, $user_id );
        wp_redirect( '/dashboard' );
    }
}
?>

Why it's vulnerable:
The code checks that a user is logged in but does not require the current password or any secondary verification before allowing the password to be changed.

Fixed pattern
<?php
if ( is_user_logged_in() ) {
    if ( isset( $_POST['new_password'], $_POST['current_password'] ) ) {
        $user_id = get_current_user_id();
        $user = get_userdata( $user_id );
        
        if ( wp_check_password( $_POST['current_password'], $user->user_pass, $user_id ) ) {
            wp_set_password( $_POST['new_password'], $user_id );
            wp_redirect( '/dashboard' );
        } else {
            wp_die( 'Current password is incorrect.' );
        }
    }
}
?>

05Prevention Checklist

Require current password verification
on every password-change form, even for authenticated users. Compare the submitted current password against the stored hash before allowing any change.
Implement CSRF tokens
on password-change forms to prevent attackers from triggering changes via forged requests from other sites.
Send a confirmation email
to the user's registered address whenever a password change is initiated, with a time-limited link they must click to confirm. This catches unauthorized changes even if the session is compromised.
Log all password changes
with timestamp and IP address, and alert the user via email so they can detect unauthorized changes quickly.
Rate-limit password-change attempts
to slow down brute-force or automated attacks on the password-change endpoint.
Consider requiring re-authentication
(e.g., a fresh login or multi-factor code) for sensitive operations like password changes, especially on high-value accounts.

06Signs You May Already Be Affected

Check your application logs for multiple password-change requests from the same user within a short time window, or from unusual IP addresses. If users report being locked out of their accounts without having initiated a password change, or if you see password-change events followed immediately by account activity from a different location, investigate whether this weakness has been exploited.

07Related Recent Vulnerabilities