from the article: "Initial fixes were in place within 24 hours." So it's all fixed? Great! I'll stick with Heroku as my hosting provider like the author does.
"If the POST was made with ID removed from both the URL and body, the password of a random account would be reset and the account automatically logged in to:"
Like most sites, after you register your account, we generate a nonce and email it to the provided address. You click the link with the nonce, we prompt you to set your password, and your account is ready. Our bug was that for already-verified users, the nonce column in the database is empty (it's a nonce, you see, so we only use it once...). This was the root cause for both bugs Mr. Sclafani describes.
If the token is nil, it basically turns into a `find_by_email`.
I'll also add that while we had tests that were intended to avoid this bad behavior, these tests were unfortunately broken and were instead verifying the incorrect behavior.
It's interesting how differently Django handles password resets - no nonce is generated.
Instead, the user is emailed a token that's just her user ID, HMAC-signed with the last login date and a secret site key.
You can't generate valid reset links without knowing the secret key and you can't tamper with the one you got because it's HMAC-signed. By adding the last login date to the HMAC you make sure the link can be used only once. After a user resets her password, the last login date is updated to now so the link is no longer valid due to broken HMAC.
I like this solution because it doesn't rely on storing any state anywhere between requesting the reset and completing it.
This is pretty much exactly what Drupal does as well, with the exception of using the user's password hash instead of the ID as input before hashing and it also stores the timestamp of the reset request as part of the URL (and the token) to allow for expiring password resets.
Thank you for being open about that issue. All the reading about vulnerabilities definitely made me more aware when writing critical parts of my applications.
If you build the query at runtime based on whichever arguments are available, it is quite easy for missing arguments to slip through to the inner query generator and end up with a blank where clause.
a blank where clause throws an error (in mySQL at least) as it should do. Or will the query generator respond to missing arguments by removing the where clause altogether? In which case that's some pretty nasty behaviour.
I wish that Heroku would just use a third party open source auth system like Persona. This stuff is difficult and complicated to implement correctly. Sure, Persona could have had this same bug, but I'd rather trust someone who's job all day long is to implement auth systems (and which I can get the source to).
http://blog.heroku.com/archives/2013/1/9/password_hijacking_...
Thanks again to Stephen for giving us ample time to fix this.
-Tom Maher, Heroku Security Team