Magic Link Token Leakage: Passwordless Logins Gone Wrong
Passwordless login trades a password for a one-time token in a URL. The user clicks a link in their email and they're in. Convenient — and fragile, because a login secret now lives in a place that URLs go: browser history, server logs, referrer headers, link scanners, and chat previews. If that token leaks or lingers, it's account takeover.
Treat the token like a password, because it is one
A magic link is https://app.example.com/auth/verify?token=abc123. Whoever submits abc123 becomes the account owner. So every property you'd demand of a password reset applies here: single use, short expiry, bound to the requester, and never exposed to a third party.
Referer leakage — the quiet one
Open the magic link and watch what the landing page loads. If the verify page pulls in third-party analytics, ad pixels, or external fonts before it strips the token from the URL, the full link travels to those origins in the Referer header. A token you thought was private is now in someone else's access logs.
Test it: click your own magic link, then inspect outbound requests in DevTools for a Referer carrying ?token=. Check the page's Referrer-Policy — a strict policy like no-referrer or strict-origin blunts this; its absence is a real leak vector.
Single use and expiry
The two tests that catch the most:
- Reuse. Consume the link, then submit the same token again. If it logs you in a second time, the token isn't single-use — anything that captured it once (a log, a proxy, a shared history) replays into the account.
- Expiry. Request a link, wait a day, then use it. Login tokens with no expiry or a multi-day window give a leaked link a long life. Confirm the actual window rather than trusting the docs.
Also check whether requesting a new link invalidates the old one. If a user requests three links and all three still work, you've tripled the leak surface.
Prefetch, scanners, and predictability
Email clients and security appliances follow links to scan them. If a scanner "clicks" the magic link, it may consume a single-use token before the user does — a denial of service — or, worse, the app may treat the scan as a login. Test how the app behaves on a bare GET (require a POST confirmation step to defeat automated prefetch).
Predictability is the jackpot: if tokens are sequential, timestamp-based, or short, you may be able to guess or brute-force one for an account you don't own. Generate several links for your own account and eyeball the entropy. Never brute-force against a real user — demonstrate the weak generation with your own tokens and let the math argue impact.
Binding and interception
Does the token care who submits it? A link requested on your laptop that logs in from an unrelated network and device suggests no binding, which makes a leaked link usable anywhere. Some designs bind the link to the originating session or require the same client — worth noting either way.
The report
Two owned inboxes make this clean. Show the link, the leak vector (a Referer capture, a reused token, a guessable pattern), and the resulting authenticated session. State impact bluntly: leaked or replayable magic link equals full account takeover, no password required.
Recommend single-use tokens, short expiry (minutes), high entropy, a POST-based confirmation to defeat prefetch, Referrer-Policy: no-referrer on auth pages, and invalidating prior links when a new one is issued.
MDN's Referrer-Policy reference explains which policies stop the URL from leaking to third parties — the single most common way these tokens escape.