Secure Account Recovery Made Simple
paragonie.com
paragonie.com
He's right: don't ask for security questions from your users. Security questions are ridiculous.
He's wrong about making password reset optional. In 10+ years of consulting at Matasano I can't think of a single application for which users could sign up directly that didn't have password reset. You're asking for a support nightmare by making it optional --- worse, you're putting your engineering team in a position where they can be socially engineered into changing passwords, and this is an attack vector that actually does get used in the wild.
The password reset scheme here is, I think, overcomplicated. If you generate a long random token and associate it with the user's account, there's no significant risk of timing attacks (I'd go a step farther and suggest there probably isn't a realistic risk of timing attacks against tokens either way, but I'd want to confirm that suspicion with some research first). Just look up the reset by the token value, invalidate the reset record (you want to save them anyways), and then check whether the email matches.
If you really, really wanted to be paranoid about it, you could just HMAC the token against a secret you pass in through the environment, so that the attacker can't even map inputs to database entries (in the same manner as they can't meaningfully launch a timing attack against a password hash).
But nerdery aside, unless I'm missing it, this piece seems to be lacking one of the most important controls for password resets:
Invalidate reset records after use, after any password change, and after a fixed (short) amount of time.
It is amazing how many applications have password reset schemes that leave login-equivalent data in people's email boxes for all time.
> Invalidate reset records after use, after any password change, and after a fixed (short) amount of time.
That was (somewhat implicitly) covered in the "what incumbent systems" do: https://paragonie.com/blog/2016/09/untangling-forget-me-knot...
Since you mention it, I'll make that a separate list item. It's important enough to risk being repetitive about.
I would say the first most important attribute of a password reset scheme is that it not be based on weak secondary passwords like "Mother's maiden name".
The second most important attribute of a password reset scheme is that it be based on a simple lookup of a random token, and not elaborate encryption schemes, since those schemes tend to cough up game-over auth bypass bugs.
The third post important attribute of a password reset scheme is that they be single-use and rigorously invalidated, so that your application isn't spewing hazmat over people's email spools.
https://paragonie.com/blog/2016/09/untangling-forget-me-knot...
I am starting to think this is the most important thing. Every time I read about an attack or a vulnerability 2 questions pop into my mind.
1. How easy is this to exploit remotely?
2. Is this an attack vector that actually does get used in the wild?
I usually finish both those questions off with "Today and what about next year?"
e.g. It's interesting that quantum computing is going to change quite a few things, but my list of worries is about 1,000 things long, and that's just way too far off to worry about today, or next year.
The attacker compromises the account and generates password recovery tokens.
He then blackmails the user if the user resets it trough support or via account recovery the attacker uses a token to gain access to the account again.
This is a very common tactic in blackmail for online game accounts.
In fact this is also often used to bypass other security controls such as auto account locking.
You compromise the email account, log on the IP or anything else triggers an automatic account unlock the user clears things up with support and the attacket uses the reset token to access the account without triggering a lockout this time around.
If you're building a typical e-commerce platform, no, it won't. That's why the recommendation is so watered down with "if reasonably practical" and has entire sentences dedicated to "it's OK if you default to Yes, but please give them a choice".
But if you're building the next Silk Road? You should default to opting users out unless they check the opt-in box.
I can see the potential for that to be true, but I don't think there's any evidence that this is necessarily true.
Specifically: If you bury the option where only power users (i.e. the kind of users who are likely to use GnuPG and KeePassX) will trivially find it, the sort of folks who never change defaults would leave it enabled.
If an attacker provides an invalid token, the record wouldn't be found in the database, and the web app would return an error indicating an invalid token. When a valid token is provided, the user is authenticated. You would immediately know whether a token is valid or not—no timing requiring.
However, perhaps there is potential for a timing attack during the initial stage of the reset process, when the user is asking to enter their email address? If the email address provided exists in the database, the server has to 1) generate a token, 2) save the token in the database and 3) send out an email with the generated token. If the email address doesn't exist in the database, the server doesn't have to perform any of these functions.
Potentially, couldn't this allow an attacker to enumerate the email addresses in the web app's database? Of course, in and of itself, this wouldn't allow an attacker to access any of those accounts. And the split-token method suggested by the article wouldn't prevent this enumeration issue.
Is there further potential for a timing attack that I am missing?
EDIT: fixed a typo
Hashing the token protects against this.
For more detail: http://blog.ircmaxell.com/2014/11/its-all-about-time.html
{ "account_id": 12345678, "password_reset_token": "bohghai6fui1Ma7ozaF3nu2PheV2eeroh8daonoh6ceiCub3joengei2Lohhu8ti", "exp": 1474737955 }
Set the expiration to be 5 minutes from the time the token is generated. This leaves open the possibility that the same token could be used to reset the user's password multiple times, but you could avoid that by including the hash of the user's previous password in the token, or by keeping a "password changes" counter in the database and including that value in the token.
0: https://tools.ietf.org/html/rfc7519#page-9 or https://jwt.io
The "split token" password reset is snake oil. Just store the hash of the token (ideally stretched like any password) in the database and mail the original token out. No need for "split tokens". A password reset token is a temporary password and should be treated like one.
If I understand it correctly, that's exactly what they propose. You mail the original token out, store its hash. The second string is used as an ID (there should be a secondary index on it).
One of two things just happened:
1. You're being intentionally offensive where it's not really warranted
by calling this proposed strategy snake oil.
2. You don't know what snake oil means.
I'm going to assume the latter. Here's the thing: - Hash functions are deterministic. You'll make timing attacks more
difficult, but you aren't removing the underlying timing leak.
- You database engine almost certainly doesn't compare strings in
constant time in SELECT queries, so there's almost always going
to be a timing leak in SELECT queries.
- Separating the timing leak from the authentication (and making the
latter constant-time) properly alleviates this risk.
Other solutions (relying on the avalanche effect of hash functions, using a random sleep, or sleeping until a minimum amount of time has passed) are brittle and harder to reason about than solving the problem directly, which is what the article proposes.See also: http://stackoverflow.com/a/28486617/2224584
You might disagree with the technical details and arguments laid out, but it isn't snake oil.
Can you explain in more detail why base64 makes you "cautious?"
> Just store the hash of the token (ideally stretched like any password) in the database
That's actually LESS secure. Their scheme has an ID (or "selector") and a hash ("verifier"). This means you can limit attempts against a single ID/account and also aren't going to compare a hash entered to every record in that table.
What you're proposing is massively weaker than what they propose. It also has timing attack problems.
That is why the use of base64 makes this look suspect even though on close inspection the ideas are probably valid.
It's about an implementation of RFC 4648 encoding (including base64, base32, etc.) that doesn't index based on secret data.
Consequently, if a practical cache-timing exploit is ever demonstrated in existing implementations of encoding functions, the open source library we wrote will be immune.