Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Notice of Security Incident (flipboard.com)
46 points by captn3m0 on June 5, 2019 | hide | past | favorite | 23 comments


> If users created or changed their password after March 14, 2012, it is hashed with a function called bcrypt. If users have not changed their password since then, it is uniquely salted and hashed with SHA-1.

This is a common mistake.

Don't ever do opportunistic upgrades. Instead, upgrade all at once and retain "legacy" metadata needed to re-authenticate the user until their password hash is rotated. https://paragonie.com/blog/2016/02/how-safely-store-password...

Beyond that: Bcrypt is great, SHA1 is terrible, and the level of technical detail in this advisory is excellent. The fact that I was even able to critique their 7 years old security decision at all speaks volumes to the transparancy.


I think their main mistake was requiring a user to change their password to upgrade the hash instead of upgrading the hash on the next user login.


What if there wasn't a "next user login"? You'd hold onto insecure hashes possibly forever.

Don't make this mistake. Rehash all of them up front.

The details of whether or not users are forced to change their password is irrelevant to the escape route from this trap.


How are you going to rehash them without having the cleartext password? Hash the SHA1s to bcrypt and then check every password's bcrypt and SHA1*bcrypt's password from then on?


It’s explained in the linked blog post in the chain you’re replying to, but:

- Set all password hashes to Bcrypt(existing SHA-1 Hash)

- Create a new flag column to track whether the password is upgraded to straight Bcrypt.

- If flag is false, compare stored hash with Bcrypt(SHA-1(user input)). If it matches, log user in and replace stored hash with Bcrypt(user input). Set flag true.

- If flag is true, compare stored hash with Bcrypt(user input)


Genuine question: does hashing with algorithm B an existing hash computed with algorithm A solve anything at all ?

I mean, A’s hash collision issues will just be applicable as well into B’s space, right?

And I would go for rehashing at next login, and, after a while, identify and disable (no login possible) the legacy accounts with their deprecated hash, forcing users to change password at their next login (if it comes one day, that is).

(Also, I would not use a Boolean, but some integer to identify what is the current algorithm to use, to cope with future deprecation, but really a small implementation detail here)


> Genuine question: does hashing with algorithm B an existing hash computed with algorithm A solve anything at all ?

Absolutely. It solves by far the #1 threat against password hashes when they get leaked.

Algorithm A here is SHA-1, which is very fast to hash, which makes it completely terrible for password hashing -- when an attacker gets their hands on the hashes and salts, with a halfway decent GPU they can dictionary attack and recover even very long passwords in just a few hours, and then merrily use the same password get into the email account, because to a first approximation everyone is terrible about password re-use.

Algorithm B here is BCrypt, which is very very insanely slow to hash, so the same attacker would need years/decades to recover the passwords from the hashes.

HashAlgorithmB(HashAlgorithmA(input)) is dominated by Algorithm B's runtime, so it is equally very very insanely slow to dictionary attack the hashes.

> I mean, A’s hash collision issues will just be applicable as well into B’s space, right?

Sure. But the threat here isn't collision generation at all -- that's more of a PKI infrastructure concern.

Nobody is worried that the guys who have these SHA-1 hashes are going to sit around generating collisions -- that would be slow and stupid, the collision would only apply to other SHA-1-using websites that use the same salt. For this reason, nobody cares about password collisions for leaked password hashes.

They're just going to throw dictionaries + listed salts into a GPU and generate SHA-1s at a rate upwards of 68 billion per second on a cheap GeForce 1080, and quickly reverse the entire table. That gets them into every site the same password was used for, no matter the hash function.

This is how all of those leaked password DBs get generated, and this is why people say “don’t use SHA-1, use BCrypt for passwords” in the first place. It’s all about speed being bad, not collisions.

This is the threat BCrypting SHA-1s guards against.

> And I would go for rehashing at next login, and, after a while, identify and disable (no login possible) the legacy accounts with their deprecated hash, forcing users to change password at their next login (if it comes one day, that is).

You're still keeping easily-reversible hashes around for some period of time that cost users control of passwords if-and-when they leak before you wipe them. You can do better with zero downside. Get rid of them all immediately.


Yep, I see, it makes total sense. Thanks for the explanations!


I don't think that'd work in this case since the SHA-1 was salted, so you'd need to store the SHA-1 salt in a separate column, then calculate Bcrypt(SHA-1(input + salt)), right?


Obviously they had to be storing the salt to begin with, so yes you’d keep using that salt column and use user input+salt as the input to SHA-1.


The legacy_password flag can also be a text field containing a salt. If it's empty, you have a non-legacy password.


Yes, and slowly upgrade from bcrypt(sha1(password)) to just bcrypt(password) as uses reenter their password. Do the same when you finally upgrade from bcrypt to whatever is next. No harm in specifying the encryption 'state' in your database for each user.


Half measures are not solutions. Keeping insecure hashes on hand was the main mistake, full stop.

They'd still have leaked all of the insecure hashes of people who never logged in after the change in 2012 -- and that's still not good enough.

Immediate remediation means never keeping toxic hashes on hand, and never having to say to any subset of your customers "Sorry, but your password has been compromised due to our incompetence".


Popped in to say exactly this.

If you ever come across insecurely hashed passwords in your career, always remediate all of them with a second more secure hashing strategy at once.


For an app that I no longer use, long since deleted and have forgotten about, hearing that there was a security incident that compromised my password is unsettling. I was relieved that I logged in to their app through twitter and my password wasn't store through them.


Same here, at least for the first part. I had used a plain email/password to sign up. After getting this advisory I reset my password and immediately deleted my account.

Nothing against Flipboard, and I appreciate the level of transparency here, but it's annoying to think that I created that account 7 or 8 years ago, haven't touched it since, and it's still there, with the potential to be a liability to me.


Why does it matter? Do you use the same password everywhere?


Bummer for the company and I wish them well as they navigate the incident response waters. What I found interesting from the article was

If users created or changed their password after March 14, 2012, it is hashed with a function called bcrypt. If users have not changed their password since then, it is uniquely salted and hashed with SHA-1.

They could have migrated all accounts regardless of whether they came back to the site or not, changed their password or not, etc. Just use:

password_hash = bcrypt(sha1(raw_password))


What can someone use as a salt that is unique to a user that would not be in that record as plain-text and guess-able? Creation timestamp? email? username?


Just use a random value and store it with the hashed password. The salt is there to prevent an attacker from using a rainbow table to search your whole database for known passwords. Even if they have all your hashes and all your salts the fact the salts are unique forces them to attack each hash individually.

What you're looking for is a pepper [0]

[0] https://en.wikipedia.org/wiki/Pepper_(cryptography)


Hmm, seems to me using both is optimal

horrible psuedocode follows...

  cryptchoice = len(username) % 3

  switch cryptchoice:
     case 0:
       pepper = pepper[1]
       salt = email

     case 1:
       pepper = pepper[0]
       salt = creation_time
       
     case 2:
       pepper = pepper[2]
       salt = username
       
  bcrypt(salt+password+pepper)


It's definitely wise to use both a salt and a pepper when hashing passwords. Don't bother with the switch statement, though, it's just security through obscurity. You're much better off keeping your security-related code as simple as possible so it's easier to notice bugs in it.


This just made for a good reminder I needed to close this account.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: