Ask
29

I built password reset in an afternoon and I am not confident it is right - what are the parts people get wrong?

Small application, own authentication. Reset works: you enter your email, you get a link with a token, you set a new password.

It has been live for months and I have never gone back to it. Reading about it now, I have a growing list of things I am not sure about - how long the token should last, whether mine can be used twice, what the page should say if the email does not exist, and what happens to someone who is already logged in elsewhere.

I would rather fix it properly once than keep discovering gaps.

What does a correct reset flow actually look like, and which of these details matter most?

3 answers Share
Report

Answering anonymously, a moderator will review it first.

  • @enumeration_leak · 3w ago

    On what the page says when the email does not exist: always the same message, whether the account exists or not.

    If you say no account found for that address, you have built a tool that tells anybody whether a given person has an account with you. That matters for any service, and it matters a great deal for some.

    So the response is always something like: if an account exists for that address, we have sent a link.

    Things that leak the same information accidentally:

    Response timing. If the existing-account path sends an email and the other returns instantly, the difference is measurable. Doing the work asynchronously for both, or adding a consistent delay, closes it.

    Different status codes or redirects between the two paths.

    Rate limit messages that only appear for real accounts.

    The signup form, which frequently says this email is already registered - closing the leak in one place and leaving it open in another achieves nothing, so this is worth checking at the same time.

    And rate limit the reset endpoint per address and per source. Without it, it is a way to send somebody a lot of email, and you are the one who gets reported for it.

    26
    Share
    Reply

    Answering anonymously, a moderator will review it first.

    Report
  • @single_use_expiring · 3w ago

    The token has four properties and yours needs all of them.

    Unpredictable. Generated from a cryptographically secure random source, long enough that guessing is hopeless. Not derived from the user id, the email, or a timestamp.

    Single use. Invalidated the moment it is successfully used. This is the one people miss most often, a reused link means anyone who ever sees that email can reset the account again later.

    Short lived. Somewhere in the region of fifteen minutes to an hour. Long enough for a real person, short enough that an old email in an inbox is not a standing key.

    Stored hashed. Store a hash of the token, not the token itself, and compare hashes. Your database is then not a list of working reset links, which matters enormously if it is ever exposed.

    Also:

    Invalidate all outstanding tokens for that account when a new one is issued, and when the password is successfully changed.

    Put the token in the path or a query parameter you handle carefully, and be aware it can end up in logs and in referrer headers: a redirect to a clean URL after consuming it is a small thing that helps.

    Single use and short lived are the two that turn a working flow into a correct one.

    30
    Share
    Reply

    Answering anonymously, a moderator will review it first.

    Report
  • @after_the_reset · 3w ago

    The part your question already sensed, and it is the most interesting one: what happens to existing sessions.

    The scenario that matters: somebody else has access to the account and is signed in. The owner resets the password. If existing sessions survive, the attacker is still logged in and the reset achieved nothing.

    So on a successful password change:

    Invalidate every other session for that account. With server-side sessions this is a delete. With stateless tokens it is harder - you need either short expiry plus a check against a value that changes on password change, or a revocation list. This is one of the clearest practical arguments for keeping server-side sessions in a small application.

    Keep the current session signed in, or sign them in fresh. Signing the user out immediately after they set a new password is a common and irritating choice.

    Email the account to say the password was changed, with a note about what to do if it was not them. This is the one control that catches an account takeover the owner would otherwise never notice.

    Also worth doing: require the current password to change it while signed in, and re-authenticate before changing the email address - because an attacker who can change the email owns the reset flow entirely.

    1
    Share
    Reply

    Answering anonymously, a moderator will review it first.

    Report