
Everything you ever wanted to know about building a secure password reset (2012) - colinprince
https://www.troyhunt.com/everything-you-ever-wanted-to-know/
======
astura
Fuck "secret questions."

I don't remember if i answered "88 Oldsmobile Cutlass Supreme," "1988
Oldsmobile Cutlass Supreme," Cutlass Supreme," "'88 Cutlass Supreme," "cutlass
supreme," "1988 Cutlass Supreme," "Oldsmobile" or any other variation or
misspelling. I don't remember if i had to tweek my answer to fulfill your dumb
requirements (my favorite teacher has a special character in her name! Or the
answer is 3 characters long and you have a minimum of four! My elementary
school is numbered) I remember what street I lived on in middle school NOW,
but I'll probably forget it in five years, I routinely forget much more
relevant things. On top of that maybe I just don't want to tell every website
I use who my childhood crush was. There's also the problem of most "secret
questions" that are available being irrelevant for X% of the population. (ex.
loners and people who didn't have a stable home life... they rarely include
stuff like "who was your foster mom when you were 8?" Even the international
crowd could have trouble with most questions.)

I'm old enough and have enough online accounts (I have 246 accounts saved in
my password manager) that i now just write "fuck you" for all the answers and
put 'all answers fuck you' in my password manager. Because these questions
stress me out way too much and I'm constantly bombarded with them. Answering
them becomes a major pain point when everyone wants them. I've completely lost
it with the damn "[not so] secret questions."

~~~
o10449366
Even worse are websites/services that impose ridiculous character requirements
(you must include 2 symbols, 3 numbers, 5 letters, 2.5 of which must be upper
case and positioned at the prime number indices, oh and you have to change
your password every 6 months) and then limit your password to 8-12 characters.

The way I remember passwords is by stringing together random English words
with random special characters inserted throughout. This means most of them
are 30-40 characters. I find these far easier to remember than my PayPal
password, which is arbitrarily limited to a random collection of special
characters that must be of length less than 12 (or whatever their ridiculously
short limit is.)

If you truly care about the security of my account _don 't impose character
limits_.

~~~
cmurf
I spent 20 minutes trying to come up with a password for chase.com recently
because of insane rules like, you can use more than two characters in
ascending or descending order, can't use the same character more than twice.
Using a random password generator above about 20 characters, and it's
guaranteed to break one of there inane rules; let alone if you want to use a
pronounceable passphrase that's simply long. It's really aggravating that big
companies are some of the worst at this security stuff.

~~~
Blackstone4
I use Bitlocker and I can set limits/conditions on pwd generation which is
then saved under a master pwd.

~~~
paulryanrogers
Which Bitlocker is that? I'm only familiar with drive encryption by Microsoft.
Do you mean Bitwarden?

~~~
Blackstone4
Opps you're right....BitWarden

------
njarboe
I sure wish my brokerage would allow me to turn off all online password reset
options. They have an office near to me. If I forget my password, I would like
to have to go to the office with my passport to reset it. This person should
be high level and well trained so that social engineering is unlikely to work
on them. I would be happy to do this and would prefer if they charged me $100
to reset my password this way. I don't day trade so I don't need a way to
instantly reset my password. Many years ago I reset my brokerage password
online and it was way to easy to do.

The amount of money I have in the account is small enough that I have not
seriously looked into other options yet. How do people with $5 million in a
retirement account deal with the fact that someone could really screw you over
by getting into the account?

It might be somewhat difficult to get money out of the account, but if you are
just trying to hurt someone, that should not be too hard. Log in. Use a
million dollars to buy a bunch of a penny stock and the price will skyrocket
as you buy. Then start to sell and keep selling as the price plummets.

~~~
rhacker
I've actually wondered the opposite. If someone is charming enough, and has
done some preliminary research on the bank's in-person screening, could they
bypass security with something as little as a fake ID?

Overall, I don't know if there is a perfect solution. Perhaps for banks we
should pee-in-a-cup DNA test in addition to other tests. For a porn site we
can do a poor mans email reset.

~~~
_dark_matter_
I believe that DNA tests fail the same reason that eye scanners and
fingerprints do: they are not private. Almost anyone can get access to any of
those 3, and then they just need to figure out a way to use it on the system.

~~~
tedunangst
Is it really that easy to forge someone else's DNA and bring it to a testing
facility?

~~~
tialaramex
DNA is a replicator molecule. You don't need to forge it, just get a tiny
sample and it will make as much as you could ever need with gear found in any
high school lab.

------
rraval
I disagree with the section titled "Username enumeration and the impact on
anonymity".

The author is trying to make the point that you shouldn't tell users that
their username is invalid in a password reset flow. The author then goes
further and recommends obscuring error messages during the login flow as well.

I find this behaviour to be user hostile and not particularly effective at
concealing anonymity. Under the same threat model, a suitably motivated third
party can simply attempt to sign up with the same username, and you'll
definitely have an error message if the user is already signed up (and that's
besides the point that just because a username exists doesn't mean the person
behind that identifier actually signed up, anyone could've done it).

The author tries to justify their position by calling it a "slight usability
tax" and "a small trade-off for an infrequent process", but my very short
experience building a consumer oriented SaaS is that:

1\. Password requests are frequent, even with ~5k users

2\. Lots of people have more than one email address, and frequently forget
which one they used for your service.

3\. The support burden simply isn't worth the pseudo-anonymity.

~~~
OliverJones
I built a password reset system that didn't tell the requestor whether they
gave an unregistered email address. It just said, "look in your mailbox."
Because, avoid telling cybercreeps anything useful.

But my tech support team screamed about it. So, I changed it to send an email
to the unregistered address saying

"somebody asked for a password reset to be sent to this email address. But we
don't have an account associated with this address. If you need help please
hit URL or call PHONE. "

A cybercreep still can't tell from the password-reset page whether the email
was correct.

This ruse solved my support team's problem.

~~~
ergothus
> This ruse solved my support team's problem.

Having places where I don't recall if I have an account, and if I do, which of
my email accounts it was registered to - this solution would actually make me
happy. I recall running into it once or twice in the wild and was always just
as happy to get the "we don't have an account with this email message" in
either email or immediate feedback. That said, I agree with the poster that
sees the actual security of hiding usernames as marginal at best, outside of
sites where the mere presence of a user is indicative. ("Johnny, why do you
have an account on "ILikeLeadingCommas.com"?!")

------
protonfish
I really think secret questions are garbage. They grant minimal extra security
for the cost of a huge hassle for customers. But the worst part about them is
requesting and storing a significant amount of highly personal data. The
likelihood of this data being breached and used against you is probably much
greater than the security improvements it grants. Requiring many pieces of
personal data to sign up for every fly-by-night web application is short-
sighted and inconsiderate.

~~~
jechamt
I agree completely.

The article above is pretty dated (which is noted in the post), and so leaves
out some better options for security that are available today, such as
authenticator apps for 2fa. In the context of when it was published, it was an
excellent and important stepping stone in security awareness, but working with
the tools of the time.

Now, we have better options and Troy Hunt (among others) has covered many of
them in separate posts over the years.

------
lisper
Another criterion for a good security question: make sure everyone actually
has an answer. I use a website that forces me to answer the following
question: in what month was your first child born? It boggles my mind that
someone at this company could fail to consider the possibility that a user of
their site might not have children.

~~~
rhacker
That's kinda funny to me. For people like me (and likely a lot of others that
browse this site) the answer to how many children you have, is clearly
"@#T%FGDw34dsd$"

~~~
koolba
Giving answers like this over the phone to customer services reps is always a
good laugh.

CSR: “ _Wait, what’s your favorite city?_ ”

Me: “jeogusbw378fnsbwxwobp”

CSR: “ _Is that in Europe?_ ”

------
munchbunny
This is really good stuff. I think the only thing I'd add is that, if you're
building a login system anew, you _should_ do 2-factor, and you _should not_
use SMS codes. Use phone authenticator apps (time-based OTP's), FIDO U2F,
heck, even Smartcards if you're paranoid.

The best implementations I see are the ones that allow both phone
authenticator apps for the people who don't want to carry a keyfob around or
need to log in via their phones, and U2F for people who want their physical
factor separate from their phone.

~~~
tialaramex
OTPs are phishable. If you worry about SMS codes, you shouldn't accept OTP
either, bad guys are just going to socially engineer your user into giving
them a valid code.

FIDO is the way forward, but having run into Microsoft's hapless first attempt
(since they managed to get it to not work in anything except Edge on specific
builds of Windows they also made it optional, so it actually can't make your
security better. Brilliant) I have less confidence it'll ever be useful to the
ordinary person... Still, it does protect my Google account.

~~~
munchbunny
Phone based TOTP's are vulnerable to real-time phishing, but they still
require you to sit there and enter the number on your phone. It still has to
be _your phone_. That's not ideal, but that's substantially better than SMS
where someone can take your phone number and your second factor is gone. No
real-time requirement.

That makes phone based TOTP the most secure second factor that is pretty much
universally accessible to lay users (i.e. not buying a dedicated second factor
device, and most people have smartphones).

FIDO U2F isn't quite there yet in terms of universal support, but its design
is definitely promising from a usability perspective. It's one technology I'm
willing to hold my breath for.

------
BerislavLopac
I've been wondering how even having a password at all might be an antiquated
feature.

Most sites/applications today require you to have an email address (whether
alongside a username or, increasingly, as the only identifier), and fully pass
all verification to your email provider (by providing a password reset). In
that situation, having a separate password for that site (and, presumably, one
for each new one) seems a bit superfluous -- it should be enough to, instead,
just enter your email and receive a unique one-time URL that will log you in
if opened within, say, 15 minutes.

Would this system be any more insecure than the present model of email +
password + reset link?

~~~
PretzelFisch
How would you get that to work simply in a mobile app login flow?

~~~
andreareina
IIRC, slack does (or did, ~3 years ago) exactly that. Either the email link is
structured to cause the mobile OS to open the app, or the link leads to a page
that then presents a link that opens in the app. In either case the link(s)
contain a token that's valid for one use.

------
eknkc
My current approach is to send an email with a temporary password which works
for a single login and can be used to change the password. Original password
still works. Temp password expires in 1 hour.

It is basically a reset token but allows logins instead of loading up a reset
form. One less thing to implement.

~~~
eterm
Firstly, does your approach still invalidate the original password? If not
that's really counter-intuitive. If I click a reset password button I want my
password reset.

Secondly, it sounds like you just give auth based on this token so you can re-
use your password change screen, but if your password reset screen doesn't ask
for the current password it really ought to do so to prevent the risk of left
over sessions being used to hijack accounts (e.g. not logged out public
machines, stolen cookies, etc).

~~~
eknkc
Well the idea is that you do not click a reset password button. You click on a
forgot password button so this gives you a temporary one to use.

Your second point is spot on and I do ask for the current password. The temp
password also works there. This is counter intuitive but not a security issue
as you mentioned. I guess I can just skip asking the old password in case this
particular session uses a temp password.

I like reusing the password change form. But there is something more to this.
Our logs show that if a user needs a password reset, they tend to need it more
than once. So, there is a pattern that some of our users simply keep
forgetting their password. It might be the nature of what we work on though,
most of our users log in a once or twice a month. That pattern gave me the
idea that if they are gonna keep forgetting it, maybe I should not force them
to reset. Let them use a temp password every single time.

It’s like what Slack does with “magic login link” stuff where you can elect to
receive a login link via email instead of using your password during login. I
guess branding it like Slack does might be better though.

------
yetanotherjosh
Good, extensive write up. Note that correctly hashing passwords is a topic in
itself and the short answer IMO is: use bcrypt.

It should also mention that after a user changes their password, all login
sessions should be invalidated. This lets a user who thinks their
account/password may have been compromised prevent an attacker from using
prior stolen sessions.

This is accomplished by including a nonce (a "number used once" which is
basically just a large random number) in the user record that is also included
in the auth session, and verified on each request. It gets set to a new value
on password reset, and thus invalidates existing sessions. (The active session
issuing the password change should ideally have its value updated to the new
value automatically, though.)

~~~
hombre_fatal
Or execute sql "update active_sessions set expired_at = now() where user_id =
$1"

Expiring all sessions should definitely be a critical point on anyone's
checklist when building a password reset system.

Spotify didn't have this for the longest time and it was insane. Someone could
buy a hacked account for $1 and get pretty much unlimited access. Though a
special sort of infuriating softwaregore because you had to listen to what
they were listening to.

~~~
eknkc
If you do not have a server side session store, only option is to use a nonce
of some sort though.

~~~
ben-schaaf
Or use a access/refresh token pair

------
rhacker
There are some slight enhancement that could be made, but would require
gathering some additional info about your users:

Start collecting IP addresses that the user has typically connected through.
It's not OK to block that user from doing a password reset on an account with
a new IP, but you could at least do some more internal alerts and secondary
human checks (even if after the fact). Do all the other things in the article,
but this would save some people.

Another one would be to record in session storage or cookie storage, some
randomly generated key, that is permanently recorded on the server. If a
browser with any of those keys come back and initiates the forgot password
script, again, you can lower the weapons a little after they get in.

------
heyjudy
Password storage (hashes only): PBKDF such as argon2i and salted.

Limit the rate of both reset and reset re-email attempts (X within Y).

Email the link that should expire on first use and set an expirable token
param to complete the reset.

Required login again to make sure people know their password.

Form POST.

Platform-Agnostic Security Tokens (PAST), not JSON Web Tokens (JWT) for
headless API calls.

CSRF (form hidden) token.

Always https. Disable http.

------
lucb1e
Please don't use security questions:
[http://lucb1e.com/?p=post&id=65](http://lucb1e.com/?p=post&id=65)
(coincidentally also from 2012)

It's really weird to see this being advised. I thought we all agreed security
questions are bad for lots of reasons.

------
tzs
The fluffy bunny option does not work for me, in either Firefox or Chrome on
my Mac. Instead of replacing the image on the page, it just reloads the whole
page with the boring humans.

If you want to see the bunnies, it works to go the page at archive.org [1] and
click the link there.

[1]
[https://web.archive.org/web/20170819153353/https://www.troyh...](https://web.archive.org/web/20170819153353/https://www.troyhunt.com/everything-
you-ever-wanted-to-know/)

------
ggm
Nobody said the answers to test questions have to be truthful or make semantic
or syntactic sense.

My mother's maiden name was trick cheese lobster

------
amingilani
> the password is hopefully accompanied by a salt, each of which sit in their
> own columns

Question: there's no real reason for the hash to be in a _separate_ column,
correct? Just that it's different per record?

~~~
chias
Correct. Many hashing functions designed for password hashing build the salt
directly into the output string, in order to make it difficult to accidentally
hash passwords without using a salt. On the other hand, if you're just using a
bare hash function, you may find it convenient to store the salt in a separate
column, if only to save yourself having to split on a separator character /
character length in your application logic.

~~~
amingilani
Yeah, that's what I was I thinking. Bcrypt does that, or at least the
implementation I'm familiar with seeing in Rails. The sentence threw me off
there for a second. Thanks for the clarification.

------
deeg
A good article but needs "(2012)" in the title.

~~~
dang
Good catch!

Discussion from back then:
[https://news.ycombinator.com/item?id=4280440](https://news.ycombinator.com/item?id=4280440)

~~~
sethammons
Maybe a feature request to save you some work. On submission, based on url,
the site automatically lets the submitter know of prior submissions and if
they choose to submit anyway, then (if they do) the first comment is an auto
comment with links to previous discussions. Could also add a year to the
lookup and have that gets auto added to the title if not already present.

------
adrianhel
Everything you need to know about building a secure password reset:
Cryptographically secure universally unique id -> email.

