Live data from Hacker News

Okta Bcrypt incident lessons for designing better APIs

n0rdy.foo

151–160 of 169 posts

Re: Okta Bcrypt incident lessons for designing better APIs

#151

I strongly agree with the conclusion that the libraries should reject input they can't correctly handle instead of silently truncating it. I co-maintain a rate-limiting library that had some similar rough edges, where it wouldn't always be obvious that you were doing it wrong. (For example: limiting the IP of your reverse proxy rather than the end user, or the inverse: blindly accepting any X-Forwarded-For header, in…

If the input is 71 character, all the libraries happily accept it, but an attacker needs to guess only 1 character.

If these tools had a runtime check, then the cache key creation would have failed out.

72 is the max length of id, username, and password combined. If that combination is over 72, then failure and the cache key would not have been created. So, no, the attacker would not need to guess only one character of a password.

Re: Okta Bcrypt incident lessons for designing better APIs

#152
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;…

The value is the combination of userid, username, and password, so in threads on other platforms people have hypothesised that the developer tried to play it safe and use a password hash because of the password's presence. Also I'm not sure the average developer understands the distinction.

  > Also I'm not sure the average developer understands the distinction.
I'm an average developer. I'm not sure that I understand exactly. What should I be reading, or what can you tell me?

Thank you!

Re: Okta Bcrypt incident lessons for designing better APIs

#153
post #35

Earlier quoted context omitted.

I don't think it's a good idea for people to adopt new bcrypt constructions so that they can use it to generate cache keys (or, worse, other keys). (I need that "man standing up in the town hall meeting" meme for this.) Just use a real KDF, if that's really what you want. I'm still confused what password-derived material is doing in a Redis key.

Could you give an example of real KDF?

I'm not the person you're replying to, but HKDF and PBKDF2

Re: Okta Bcrypt incident lessons for designing better APIs

#154
post #35

Earlier quoted context omitted.

I don't think it's a good idea for people to adopt new bcrypt constructions so that they can use it to generate cache keys (or, worse, other keys). (I need that "man standing up in the town hall meeting" meme for this.) Just use a real KDF, if that's really what you want. I'm still confused what password-derived material is doing in a Redis key.

Maybe they wanted some cached data to get invalidated if users change their passwords?

Then use some other data which can act as a proxy for that, like the date of the last credential change. Using the password itself is a terrible security smell.

Re: Okta Bcrypt incident lessons for designing better APIs

#155

I'm really surprised they didn't cover PHP since (almost?) every framework uses bcrypt in php these days.

PHP's password_* functions make it difficult to misuse in this particular way. There's no function in that API which hashes a password with a controllable salt and returns the result; there's only password_hash(), which always uses a random salt, and password_verify(), which rehashes a password internally and returns a bool for whether it matched.

(It's still got the truncates-at-72 problem with PASSWORD_BCRYPT, though.)

Re: Okta Bcrypt incident lessons for designing better APIs

#156
post #106
post #51

Earlier quoted context omitted.

Simply hashing your data (using an established hashing algorithm/library combo) to later compare two hashes in order to check whether the data has changed doesn’t usually feel like rolling your own crypto.

The use case was KDF and they decided to do simple password hash signature hack instead by combining strings. They fucked it up.

Of course they fucked it up, as evidenced by their bad security incident. The only question is whether you can really chalk this particular one up to a problem with "rolling your own crypto." That mantra exists for a reason, but it doesn’t feel like it really applies this time. It seems more like they used established crypto—just not the right one for this particular use case.

Re: Okta Bcrypt incident lessons for designing better APIs

#157
post #150

Earlier quoted context omitted.

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…

Yikes. It's valid to assume "it will never happen" for 128 bits or more (if the hash function isn't broken) since chance of a random collision is astronomically small, but a collision in 64 bits is within realm of possibility (50% chance of hitting a dupe among 2^32 items).

> valid, 128 bits

The birthday paradox is a thing. If you have 128 bits of entropy, you expect the 50% mark to be proportional to 64-bit keys, not 128 bits. 64 bits is a lot, but in my current $WORK project if I only had 128 bits of entropy the chance of failure any given year would be 0.16%. That's not a lot, but it's not a negligible amount either.

Bigger companies care more. Google has a paper floating around about how "64 bits isn't as big as it used to be" or something to that effect, complaining about how they're running out of 64-bit keys and can't blindly use 128-bit random keys to prevent duplication.

> bits of entropy

Consumer-grade hash functions are often the wrong place to look for best-case collision chances. Take, e.g., the default Python hash function which hashes each integer to itself (mod 2^64). The collision chance for truly random data is sufficiently low, but every big dictionary I've seen in a real-world Python project has had a few collisions. Other languages usually make similar tradeoffs (almost nobody uses crytographic hashes by default since they're too slow). I wouldn't, by default, trust a generic 1-million-bit hash to not have collisions in a program of any size and complexity. 128 bits, even with low enough execution counts to otherwise make sense, is also unlikely to pan out in the real world.

Re: Okta Bcrypt incident lessons for designing better APIs

#158
post #128

I enjoyed the article and the detailed analysis for different languages. The conclusion is probably the part where most of the disagreement lies. API design is is not really at fault here if we consider the purpose of the API and the intended output. The API was designed to generate a hash for a password (knowledge factor) and for performance and practical reasons a limit has been picked up (72). The chances that som…

Ohhh, it's scrollable... I wondered why this small article gained so much attention...

Yeah, the fact that I can't have my mouse in the normal position and scroll the actual article was a problem 10 times or more while trying to read the thing...

Re: Okta Bcrypt incident lessons for designing better APIs

#159

I enjoyed the article and the detailed analysis for different languages. The conclusion is probably the part where most of the disagreement lies. API design is is not really at fault here if we consider the purpose of the API and the intended output. The API was designed to generate a hash for a password (knowledge factor) and for performance and practical reasons a limit has been picked up (72). The chances that som…

> limit has been picked up (72)

There's nothing wrong with a limit. The problem is that the library silently does the wrong thing when the limit is breached, rather than failing loudly.

Re: Okta Bcrypt incident lessons for designing better APIs

#160
post #157
post #150

Earlier quoted context omitted.

Yikes. It's valid to assume "it will never happen" for 128 bits or more (if the hash function isn't broken) since chance of a random collision is astronomically small, but a collision in 64 bits is within realm of possibility (50% chance of hitting a dupe among 2^32 items).

> valid, 128 bits The birthday paradox is a thing. If you have 128 bits of entropy, you expect the 50% mark to be proportional to 64-bit keys, not 128 bits. 64 bits is a lot, but in my current $WORK project if I only had 128 bits of entropy the chance of failure any given year would be 0.16%. That's not a lot, but it's not a negligible amount either. Bigger companies care more. Google has a paper floating around abou…

Hashtables have collisions because they don't use all bits of hash, they calculate index=hash%capacity. It doesn't matter, how you calculate the hash, if you have only a few places to insert an item, they will collide.
Post reply on HN