Earlier quoted context omitted.
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).
They clearly wanted something stronger than "a hash function" or they'd have reached for weaker cryptographic hashes.
Okta Bcrypt incident lessons for designing better APIs
21–30 of 169 posts
Re: Okta Bcrypt incident lessons for designing better APIs
#22Earlier quoted context omitted.
But why not bcrypt the password, but sha the cache key on top?
I guess because they didn't anticipate this flaw.
Re: Okta Bcrypt incident lessons for designing better APIs
#23The bcrypt implementation in the Zig standard library has both the bcrypt() function (where truncation is explicitly documented) and the bcryptWithoutTruncation() function (which is recommended and automatically pre-hashes long passwords).
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…
`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 function includes a `silently_truncate_password` option that is also pretty explicit.
Re: Okta Bcrypt incident lessons for designing better APIs
#24Earlier quoted context omitted.
They clearly wanted something stronger than "a hash function" or they'd have reached for weaker cryptographic hashes.
They wanted a hard-to-compute cryptographic hash function. Today, that means bcrypt or something with a KDF construction. However, they needed one with unlimited input size, which rules out bcrypt.
I don't like using thought-stopping cliches any more than anybody else does, but this design feels a little cargo-culted. All this stuff follows the more fundamental question of "why is the password mixed into a cache key"?
Re: Okta Bcrypt incident lessons for designing better APIs
#25Earlier quoted context omitted.
They wanted a hard-to-compute cryptographic hash function. Today, that means bcrypt or something with a KDF construction. However, they needed one with unlimited input size, which rules out bcrypt.
Or just a hash of the bcrypt hash, for the password! I don't like using thought-stopping cliches any more than anybody else does, but this design feels a little cargo-culted. All this stuff follows the more fundamental question of "why is the password mixed into a cache key"?
* 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.
Re: Okta Bcrypt incident lessons for designing better APIs
#26Earlier quoted context omitted.
They clearly wanted something stronger than "a hash function" or they'd have reached for weaker cryptographic hashes.
They wanted a hard-to-compute cryptographic hash function. Today, that means bcrypt or something with a KDF construction. However, they needed one with unlimited input size, which rules out bcrypt.
Re: Okta Bcrypt incident lessons for designing better APIs
#27Earlier 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.
Re: Okta Bcrypt incident lessons for designing better APIs
#28Earlier quoted context omitted.
Or just a hash of the bcrypt hash, for the password! I don't like using thought-stopping cliches any more than anybody else does, but this design feels a little cargo-culted. All this stuff follows the more fundamental question of "why is the password mixed into a cache key"?
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.
Re: Okta Bcrypt incident lessons for designing better APIs
#29Bcrypt 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;…
I’m wondering if okta was inspired by those.
Re: Okta Bcrypt incident lessons for designing better APIs
#30Earlier quoted context omitted.
Or just a hash of the bcrypt hash, for the password! I don't like using thought-stopping cliches any more than anybody else does, but this design feels a little cargo-culted. All this stuff follows the more fundamental question of "why is the password mixed into a cache key"?
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.