All you ever wanted to know about building a secure password reset (NSFW)
This happened to be posted literally, to the minute, while I was ripping out some of the advice from my codebase, so let me take exception with one recommendation:<p>If someone attempts a password reset on foo@example.com, and there is no record in the DB for foo@example.com, immediately tell them so.<p>Why?<p>1)  While this does "leak" the fact of foo@example.com not being in the DB (and, by consequence, can be used to verify whether any email is in the DB), naive implementations of the best practice will <i>also</i> leak that via a side channel.  If you would not immediately greenlight $20k to have a security consultant spend one week auditing this feature extensively to avoid the timing attack, a) your app is <i>not important enough</i> for this level of security and b) this buys you no real security, just the illusion of it.<p>1.5)  This is totally security theatre if you have a registration process which checks for provided emails already being the in the DB, for example for telling people "That email is taken."  Different route through the maze but same cheese at the end.<p>2)  You can already spearphish people for their accounts on example.org .  Start with an email list of people.  Spearphish for their accounts irrespective of whether they have them or not.  Collect wins since losses cost your botnet nothing at the margin.<p>3)  MOST IMPORTANTLY: you're buying yourself six years of frustrated emails from customers which had <i>90% totally automatically discoverable solutions</i> if you do this the "right" way.  I'm speaking from experience.  Many of your customers have multiple emails they habitually use.  If you do the best practice, every time they misremember which one they signed up with, you get a support email.  <i>This will be your #1 cause of support emails and the cost for dealing with them is astronomically higher than the minimal marginal gain in security for your users.</i><p>Edit: Omnibus response for people doubting that a side channel attack is possible:<p>The attacker wins with microsecond precision over the open Internet and nanosecond precision over the local network, which is plausibly achievable on all of the cloud providers than HNers like to host their apps at.  Microsecond precision is enough to discriminate between "record in DB" and "record not in DB" on many plausible application architectures, even if your HTML/headers returned are exactly identical for the two cases (and they frequently won't be out of the box).<p>For example, the relevant code for Bingo Card Creator includes the line<p>user = User.find_by_username(params[:email])<p>This line takes approximately 30x longer to execute if it finds a user in the DB versus not finding a user.  Why?  ActiveRecord.  You could, if you had a mind to, trivially discriminate between the two cases in under a week over the open Internet.  If you <i>really</i> wanted to ruin my day, you could get a prepaid Visa card and get a VPS from Rackspace on the same intranet, then discriminate between the two responses in under an hour, again without tripping anything.<p>Some folks think there are trivial ways to defeat timing attacks.  You are either mistaken or you have a very different view of the word "trivial" than I do.