Live data from Hacker News

Okta Bcrypt incident lessons for designing better APIs

n0rdy.foo

31–40 of 169 posts

Re: Okta Bcrypt incident lessons for designing better APIs

#32
post #16

Earlier quoted context omitted.

I guess because they didn't anticipate this flaw.

Also prehashing opens you up to an other bcrypt flaw you need to be aware of: it stops at the first NUL byte, so you need to use some sort of binary-to-text encoding on top of the hash to ensure you don't have any of those in the data you ultimately hand off to bcrypt.

It's astounding how bad the default API for Bcrypt is.

Re: Okta Bcrypt incident lessons for designing better APIs

#33
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 the other hand, why not have implementations assert if they are given a string longer than 72 chars? It feels to me like no-one would ever do that on purpose, so it's a massive issue which is easy to accidentally make with a really important function.

Re: Okta Bcrypt incident lessons for designing better APIs

#34
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 the other hand, why not have implementations assert if they are given a string longer than 72 chars? It feels to me like no-one would ever do that on purpose, so it's a massive issue which is easy to accidentally make with a really important function.

Don't disagree there. I asked my self the same question the first few times I had to use it.

Silently truncating the data is about the worst way to deal with it from a security standpoint. No idea why that decision was made back in the day.

Re: Okta Bcrypt incident lessons for designing better APIs

#35

Earlier quoted context omitted.

Yeah, I think both of the following would have worked if they wanted the password involved in a cache key and they wanted bcrypt to be used: * bcrypt(SHA-512(PW || stuff)) * SHA(stuff || bcrypt(PW)) Disclaimer: Not cryptography advice. It's still unclear to me why the password is in there.

hmac-bcrypt solves that problem very well, and should replace plain bcrypt: https://github.com/epixoip/hmac-bcrypt

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.

Re: Okta Bcrypt incident lessons for designing better APIs

#36
post #9

I am curious why bcrypt was used for hashing in the first place and not something like sha-512 Is there a reason I might be missing?

There is a discussion about that on the security stackexchange ( https://security.stackexchange.com/questions/133239/what-is-... ). The TLDR: > SHA-2 family of hashes was designed to be fast. BCrypt was designed to be slow. Slow == harder to brute-force == more secure.

Yes, and you can increase the work factor to make it slower to generate, specifically to fight against brute-force.

Re: Okta Bcrypt incident lessons for designing better APIs

#37
post #4

Earlier quoted context omitted.

Author here: thanks for reading the post. It's great to hear that Zig covered both cases. However, I'd still prefer the opposite behavior: a safe (without truncation) default `bcrypt()` and the unsafe function with the explicit name `bcryptWithTruncation()`. My opinion is based on the assumption that the majority of the users will go with the `bcrypt()` option. Having AI "helpers" might make this statistic even worse…

`bcrypt()` is bcrypt as implemented everywhere else, and is required for interoperability with other implementations. If you don't truncate, this is not `bcrypt` any more. `bcryptWithTruncation()` is great for applications entirely written in Zig, but can create hashes that would not verify with other implementations. The documentation of these functions is very explicit about the difference. The verification functio…

To be fair, I can't think of a single context where I would want to truncate a password before hashing so interoperability with other systems isn't worth letting by a dangerous edge case in my opinion. I'd rather have the system break for the handful of users with a 72+ char password than overlook a potential critical security issue.

Re: Okta Bcrypt incident lessons for designing better APIs

#38
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 the other hand, why not have implementations assert if they are given a string longer than 72 chars? It feels to me like no-one would ever do that on purpose, so it's a massive issue which is easy to accidentally make with a really important function.

It is almost never I good idea to assert in a library, unless the error is truly unrecoverable. I think returning an error code\throwing an exception would be very reasonable and a much better API than failing silently though.

Re: Okta Bcrypt incident lessons for designing better APIs

#39
post #4

Earlier quoted context omitted.

Author here: thanks for reading the post. It's great to hear that Zig covered both cases. However, I'd still prefer the opposite behavior: a safe (without truncation) default `bcrypt()` and the unsafe function with the explicit name `bcryptWithTruncation()`. My opinion is based on the assumption that the majority of the users will go with the `bcrypt()` option. Having AI "helpers" might make this statistic even worse…

Note that the "safe" version makes very bespoke choices: it prehashes only overlong password, and does so with hmac-sha512 (which it b64-encodes). So it would very much be incompatible with other bcrypt implementations when outside of the "correct space". These choices are documented in the function's docstring, but not obvious, nor do they seem encoded in a custom version.

Sounds like it would then make sense to hide crypto primitives and their footguns under a "hazmat" or "danger" namespace, sorta like webcrypto or libsodium.

So something like crypto.danger.bcrypt and crypto.bcryptWithTruncation

Re: Okta Bcrypt incident lessons for designing better APIs

#40
post #35

Earlier quoted context omitted.

hmac-bcrypt solves that problem very well, and should replace plain bcrypt: https://github.com/epixoip/hmac-bcrypt

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?
Post reply on HN