DEV Community

Cover image for Email Verification Link Leading to Forced Account Takeover
Bijan
Bijan

Posted on

Email Verification Link Leading to Forced Account Takeover

What if clicking a completely legitimate verification link from a trusted domain could silently log you out and into an attacker's account — without you realizing it?
That's exactly what this bug does.

The Bug:

A email verification flow was allowing an authenticated user's session to be hijacked by simply sending them a valid verification link belonging to a different account.
No phishing. No fake domains. Just a real link from a real service.

How I found it

  1. Register account A and verify it — stay logged in
  2. Register account B in a private window but don't verify it yet
  3. Copy account B's verification link
  4. Open it in the browser where account A is logged in
  5. Result: Account B gets verified, account A gets forcefully logged out, and the session switches to account B

Real attack scenario

The attacker registers an account with a similar looking email to the victim (s0mestring vs somestring) then sends the verification link to the victim — disguised as a support request:
"Hi, I'm having trouble verifying my email, can you help?" + LINK
Since the link is from a legitimate domain, the victim clicks it without suspicion.
From that point:

  • If the victim was buying a subscription → attacker gets it
  • If uploading files → they go to attacker's storage
  • If granting access → attacker gets the permissions

Even worse, the link uses a GET request, meaning it can be embedded as an image tag on any website, triggering the attack silently with zero clicks from the victim — a classic CSRF scenario.

Impact:

  • Forced session hijacking
  • Silent account switching via CSRF
  • Subscription theft
  • Access control manipulation
  • Data theft

Mitigation:

  1. Single-use tokens

    • Once the verification link is clicked, invalidate the token immediately. It should never work a second time regardless of who clicks it.
  2. Short token expiry

    • Verification links should expire within 15-60 minutes of being issued. A link valid for days or forever is a much larger attack surface.
  3. Session-aware verification

    • This is the core fix for this specific bug. When a verification link is clicked, the backend should check:

      • Is there already an active authenticated session in this browser? Does that session belong to a different account than the one being verified? If yes → reject the verification attempt and return an error, never switch sessions
  4. Don't touch existing sessions

    • Verification of account B should have zero effect on an existing session for account A. These should be completely independent server-side operations.
  5. Bind tokens to the registering context

    • Tie the verification token to the email address it was issued for. Clicking account B's link while logged in as account A should fail the context check immediately.

Takeaway:

Authentication flows are some of the most overlooked attack surfaces. A verification link feels safe — it comes from a trusted domain, it's expected behavior. That's exactly what makes it dangerous when session management isn't handled correctly.
Always ask: what happens if this link is opened by the wrong person?

Top comments (0)