That email-change bug you haven't caught yet

The classic: change-email without re-auth, combined with a password reset, lets an attacker take over an account.

· LoginWith team

Every account takeover researcher has seen this bug. It’s so consistent that it’s worth describing in full so you can check your own code.

The scenario

A user (Alice) and an attacker (Mallory) are sharing a browser session. Maybe it’s a cafe and Alice walked away. Maybe it’s a poorly isolated kiosk. Maybe it’s a more creative scenario. Mallory has access to Alice’s session but not her password.

Mallory:

  1. Goes to account settings
  2. Changes Alice’s email to mallory@attacker.com
  3. Clicks “forgot password”
  4. Receives the reset link at mallory@attacker.com
  5. Sets a new password
  6. Owns the account. Alice is locked out.

Every step is allowed by most apps. Let’s unpack each one.

The defense layer by layer

Require the current password to change the email

The email is a recovery vector. Changing it should require proving you know the current password (or holding a recently issued step-up-auth token). No current password, no email change.

POST /account/email
  current_password: "..."
  new_email: "..."

If the current password isn’t provided or is wrong, reject. Don’t be clever — the user can click a “change email” flow that prompts them once.

Email the old address first, with an undo window

After a successful email change, send a notification to the old email:

“Your account email was changed to j***@attacker.com. If this wasn’t you, click here to undo.”

The undo link has a 24-48 hour lifetime. If it’s used, revert the email, invalidate all sessions, and force a password reset. This is the safety net that catches the case where the current-password check was bypassed (session hijack after login, shoulder surfing, etc.).

Don’t invalidate existing sessions until the change is confirmed

During the undo window, keep the old email as the canonical account email in some form. Only invalidate sessions if the new email is verified AND the undo window passes. Otherwise, a compromised session can change the email and disconnect the legitimate user in one step.

Invalidate password reset tokens on email change

If an email change is in-flight (verification sent to new address, not yet confirmed), no password reset token should be issued to the new address until the change is confirmed.

Test it

Write an integration test:

  1. Sign in as A.
  2. Change email to B (without re-auth). Expect rejection.
  3. Change email to B with current password. Expect pending verification.
  4. Click “forgot password” before the verification completes. Expect the reset email to go to A, not B.

If any step fails, you’ve got work to do.

Want auth that just works?

Get started with LoginWith