Live data from Hacker News

Okta Bcrypt incident lessons for designing better APIs

n0rdy.foo

61–70 of 169 posts

Re: Okta Bcrypt incident lessons for designing better APIs

#61
post #60
post #56

Have they did a bcrypt(password + userId + username), it won't be so bad. Order of entropy is important. Also I'm not sure what functionality the authentication cache provides, but their use of bcrypt(userId + username + password) implies the password is kept around somewhere, which is not the best practice. OT. Has Argon2 basically overtaken Bcrypt in password hashing in recent years?

> Have they did a bcrypt(password + userId + username), it won't be so bad. Order of entropy is important. That depends on how exactly it was used. If it was simply used to check if previous authentication was successful (without the value containing information who it was successful for) then single long password could be used to authenticate as anyone.

> single long password could be used to authenticate as anyone.

Only if everyone uses the same long prefix for password.

Re: Okta Bcrypt incident lessons for designing better APIs

#62
post #3

Bcrypt is a password hash, not a KDF, which is the way it was used in this API. It's super unclear to me why they wanted a string-based KDF here at all; does anyone have more context? I've in the past been annoying about saying I think we should just call all password hashes "KDFs", but here's a really good illustration of why I was definitely wrong about that. A KDF is a generally-useful bit of cryptography joinery;…

They didn't want a KDF, as far as I know, but they wanted a hash function with unlimited input size. Including the username in the hash input gives you guaranteed domain separation between users that you don't get from salts/nonces. Its a generally good idea if you have a hash function with unlimited input size (all modern cryptographic hash functions except bcrypt have unlimited input size).

> but they wanted a hash function with unlimited input size

I'm kind of baffled how they came to use bcrypt for this. Bcrypt is not exactly subtle about only supporting 72 bytes of input. And this is at a company who provides auth as a service; I've got to imagine they had multiple engineers who knew this (I guess not working on that code). Hell, I know this and I've only used bcrypt twice and I'm nowhere near a security/crypto guy.

Re: Okta Bcrypt incident lessons for designing better APIs

#63
post #47

I can see the incident was a jumping off point to talk about bad APIs (bcrypt probably should error >72) but it sounds like the actual bug was they weren't checking the value in the cache matched the data they used in the hash for the key. The authentication cache check should survive any arbitrarily bad hashing algorithm because all of them are going to have collisions (pigeonhole principal). Even an arbitrarily 'st…

Not sure about that. A hash function suitable for security sensitive work, used properly, should make a collision so unlikely that you can basically forget it that it's even possible.

Think about it, that's what hashing passwords relies on. We don't store a plaintext password for a final check if the password hash matches, we count on a collision being basically impossible.

A hashmap is different, because it's using a much weaker hash function with far fewer security guarantees.

Plus, you're assuming the original values are even kept around for comparison. The cache key likely just mapped to something simple like a boolean or status flag.

Re: Okta Bcrypt incident lessons for designing better APIs

#64
post #13

That is such a rookie mistake. It's not some hidden information that bcrypt has a 72 char limit. Pretty widely documented in multiple implementations and languages. How does a company whose only job is security screw that up so badly?

On one hand, I have never heard of a password hashing algorithm that truncated to 72 characters. I assumed all one-way hashing functions were arbitrary length input. "arbitrary input -> fixed output" has always been part of the definition of hash, to me. On the other hand, I'm not a security developer at Okta.

og crypt does the same shit but 8 characters. weird historical artifacts i suppose.

Re: Okta Bcrypt incident lessons for designing better APIs

#65
post #61
post #60

Earlier quoted context omitted.

> Have they did a bcrypt(password + userId + username), it won't be so bad. Order of entropy is important. That depends on how exactly it was used. If it was simply used to check if previous authentication was successful (without the value containing information who it was successful for) then single long password could be used to authenticate as anyone.

> single long password could be used to authenticate as anyone. Only if everyone uses the same long prefix for password.

No. If the value of the cache key is simply true/false then someone would first login to their own account using the long password. This would result in storing:

bcrypt(longpassword + 123456 + me@foobar.com) = bcrypt(longpassword) = hash1 -> true

If they then try login as you@bar.com using same password there would be a cache lookup:

bcrypt(longpassword + 1111111 + you@bar.com) = bcrypt(longpassword) = hash1 -> true

Re: Okta Bcrypt incident lessons for designing better APIs

#66
post #17

Earlier quoted context omitted.

Begs the question of why the payload contained a password, right?

They wanted the cache entry to be invalidated when the password changed. Just using username as the key and storing the bcrypt password inside the cache entry and checking the password on load seems like a better solution if it was possible.

Storing the bcrypt password in the entry would make a dump of the cache almost as good as a dump of the password database. At least this way a dump of the cache makes the key opaque and requires you to guess both the username/id and password together, assuming they're not repeated in the cache value.

According to the security advisory this cache was for AD/LDAP delegated authentication, so they don't have their own password database with a version field or similar for sensible invalidation.

I guess the requirements could be something like:

  - different username/password combinations must have separately cached results

  - mitigate a potential data leak by putting all the entropy we have available together with the password material and using a slow password hashing function

Re: Okta Bcrypt incident lessons for designing better APIs

#67
post #47

I can see the incident was a jumping off point to talk about bad APIs (bcrypt probably should error >72) but it sounds like the actual bug was they weren't checking the value in the cache matched the data they used in the hash for the key. The authentication cache check should survive any arbitrarily bad hashing algorithm because all of them are going to have collisions (pigeonhole principal). Even an arbitrarily 'st…

Something similar happens in my company too. In a particular place, we use the hash of a string as the key in a hashmap instead of the string itself, because the hash is smaller and is easier to compare after the initial map has been made. It is a 64bit hash too. I have been crying about this everytime it comes up, and the response is, it will never happen. My problem is that we will never know if it ever happens too.

Re: Okta Bcrypt incident lessons for designing better APIs

#68
post #55
post #45

Earlier quoted context omitted.

I feel like the "authentication company should have known" thing is unuseful; most developers at "security" companies are just ordinary generalist developers. Ironically, I think they boned themselves by trying to be too clever here, not too casual.

You don't think a company whose entire reason for being is providing security services for other companies should have designs related to authentication reviewed by security experts?

I think it's good to want things.

Re: Okta Bcrypt incident lessons for designing better APIs

#69

Reminds me of when I saw a junior developer calling SHA-1 on an incrementing integer ID, with no salt. We had a long talk about it, he thought it was too "scrambled" to allow anyone to recognize what was being done. He shouldn't have been so junior, he was 4 or 5 years into his career. I had to be the bad guy and override his decision without further discussing why it was a bad idea, and I really tried for a good 45…

I've seen this before, a belief that just because the output looks random that it is secure. It's like storing license plates -- just hashing them without additional seasoning is of little use, because the number of possible license plates is so low that they can easily be brute forced.

Similarly, a developer I worked with once claimed that CRC32 was sufficient verification because CRC32s changed so drastically depending on the data that they were difficult to forge. He was surprised to find out not only is it trivial to update a CRC32, but also to determine the CRC polynomial itself from very few samples.

Re: Okta Bcrypt incident lessons for designing better APIs

#70
post #45

Earlier quoted context omitted.

HMAC-bcrypt is a more complicated version of the first construction I proposed, and it would need a rigorous cryptanalysis if someone wanted to actually use it in production. It sounds like Okta actually wanted PBKDF2(stuff) here. An authentication company should have known this...

I feel like the "authentication company should have known" thing is unuseful; most developers at "security" companies are just ordinary generalist developers. Ironically, I think they boned themselves by trying to be too clever here, not too casual.

Being too clever and being too casual are the same thing when it comes to matters of math.
Post reply on HN