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
- Register account A and verify it — stay logged in
- Register account B in a private window but don't verify it yet
- Copy account B's verification link
- Open it in the browser where account A is logged in
- 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:
-
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.
-
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.
-
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
-
-
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.
-
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)