Password-Reset Links Are Temporary Credentials: Where Tokens Leak Without Notice
A technical audit guide for recovery flows: token lifetime, single use, referrers, third parties, redirects, rate limits, and session invalidation.
A password reset often looks harmless: enter an email address, receive a message, click a link, choose a new password. Technically, however, that link is a temporary access key. Whoever obtains the token and redeems it in time may receive exactly the recovery capability intended for the legitimate user.
That is why asking only “Is the reset token random enough?” is too narrow. A useful audit follows the token through its entire lifecycle: how it is generated, where it appears, which systems can see it, how long it remains valid, and what changes after redemption.
Red flag: the token reaches more systems than expected
A typical reset link looks roughly like https://example.com/reset?token=.... That means the secret exists in a URL.
This matters because URLs can pass through many operational layers: web servers and reverse proxies keep request logs, monitoring and error systems collect request metadata, browsers retain history entries, and subsequent resources or navigations can create referrer information. RFC 9110 explicitly warns that URIs are commonly displayed, logged, and stored in places that are not designed as secret stores.
This does not mean URL tokens are inherently wrong. OWASP describes them as a practical password-reset method. The engineering consequence is that the token needs to be treated like a short-lived credential and its exposure surface needs to be intentionally small.
1. Is the token truly single-use?
A reset token should not become a second password.
At minimum, check:
- Is the token generated with a cryptographically secure source and sufficient entropy?
- Is it bound to one account and one recovery purpose?
- Does it have a short, defined lifetime?
- Is it invalidated immediately after successful use?
- Are older reset tokens invalidated when a new one is requested?
- Is the stored token or verification value handled so that a database leak does not instantly expose every outstanding reset link?
- Are requests and redemption attempts rate-limited?
OWASP recommends secure randomness, sufficient length, secure storage, single use, and expiry for reset tokens. Those are the foundation, not the whole recovery design.
2. What runs on the page where the token lands?
The most sensitive page is often not the “forgot password” form. It is the redemption page opened from the email.
As little as possible should happen there before the token has been converted into a tightly scoped recovery state. A strong design avoids unnecessary advertising tags, chat widgets, A/B testing, marketing analytics, and other third-party resources that are not required to complete recovery.
OWASP recommends an appropriate referrer policy for reset pages, and its Web Security Testing Guide advises avoiding third-party resources on those pages. A robust choice for a sensitive redemption document is Referrer-Policy: no-referrer, so the token-bearing URL is not sent as referrer information on subsequent requests or navigations.
The important point is not to rely on browser defaults for a credential-bearing page. The requirement should be explicit in the response and testable.
3. Does the token remain in the address bar after verification?
A useful implementation pattern is to use the one-time URL token only for initial verification. Once the server accepts it, it can establish a tightly restricted, short-lived recovery session and redirect the browser to a clean URL without the token.
That reduces the time in which the credential can appear in history, screenshots, copied links, support tickets, or later requests.
The recovery session should not silently become a normal authenticated session. It needs only the permission required for the intended recovery action.
4. Is the reset URL built from a trusted host?
A surprisingly persistent class of mistakes occurs before the email is even sent: the application constructs the reset URL from the incoming Host header.
OWASP warns against relying on an untrusted Host header when generating reset URLs. The destination domain should be configured explicitly or validated against a defined allowlist. Otherwise, under the wrong conditions, an attacker may be able to influence a legitimate recovery link so that it points at a domain they control.
This is a good example of why a security review cannot stop at the visible browser screen. The decisive weakness may live in the interaction between reverse proxy configuration, backend code, and the email template.
5. Does the request form reveal which accounts exist?
There is another security boundary before a token is generated: the “forgot password” form should not unnecessarily confirm whether a particular email address is registered.
Different error text, substantially different response times, or divergent response paths can enable account enumeration. At the same time, hiding account existence should not turn recovery into a confusing experience for real users.
In practice, that means consistent outward responses, controlled timing, and rate limits, while internal monitoring still captures abuse and delivery problems.
6. What happens to existing sessions after a reset?
Changing a password does not automatically solve the problem if a compromised browser session or stolen session cookie remains valid.
OWASP recommends invalidating existing sessions after a reset or giving the user a clear choice to do so. For higher-risk applications, server-side invalidation of old sessions is often the easier boundary to reason about.
The application should also clearly notify the account owner that the password was changed without ever sending the new password itself by email.
7. What about magic links?
Passwordless login links have the same fundamental property: for a short time, the link itself carries authentication value.
Many of the same checks therefore apply: strong random tokens, short lifetime, single use, HTTPS, a trusted link domain, limited attempts, minimal third-party surface, and a deliberate transition into a normal session.
The purpose is different, though. A password-reset token should authorize recovery only; a login link creates an authenticated session. That is why token scope should be explicit on the server rather than reusing one generic “valid token” mechanism across unrelated flows.
A practical ten-minute audit
With an authorized test account, you can already observe a great deal:
- Request a reset and check whether the outward response reveals account existence.
- Open the link and record the complete URL and redirect chain.
- Inspect response headers on the redemption page, especially
Referrer-Policyand caching behavior. - Use the network panel to see which third parties load before and after token verification.
- Redeem the token once, then try the same URL again.
- Request a second reset and check whether an older token still works.
- Confirm that the token-bearing URL disappears from the address bar after successful verification.
- After changing the password, test an old session in another browser.
- Sample application and request logs to see whether complete token URLs are being retained.
- Exercise invalid, expired, and already-used token paths as well as the happy path.
Production testing should, of course, use accounts and systems you are authorized to test.
What Website-Pflichtencheck would inspect
A recovery audit should not stop at the form. We inspect the chain across frontend behavior, response headers, redirects, third-party requests, and session handling. With a test account or staging access, we can also verify single-use behavior, expiration, token rotation, and session invalidation.
The goal is not to make authentication more elaborate. A strong recovery flow is deliberately narrow and predictable, and it exposes the temporary credential to as few systems as possible.
If your password-reset flow has “just worked” for years, that is precisely a good reason to test it deliberately. Recovery paths are used infrequently, which means old assumptions, forgotten tracking code, and historical redirects can survive for a long time without showing up in normal day-to-day operations.