Live data from Hacker News

Okta Bcrypt incident lessons for designing better APIs

n0rdy.foo

51–60 of 169 posts

Re: Okta Bcrypt incident lessons for designing better APIs

#51
post #6

> was used to generate the cache key where we hash a combined string of userId + username + password. Don't conceive your own cryptographic hacks. Use existing KDF designed by professionals.

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.

Re: Okta Bcrypt incident lessons for designing better APIs

#52
post #50

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…

Rainbow tables is not the (only) reason you dont want to hash something low entropy like an incrementing int, and adding a salt wouldn't make this secure. [Im assuming the usual definition of salt where it is known by the attacker... a pepper would be fine]

I agree, and I guess I did use salt differently than how most people see it, rather than how it is most effective. I never stored the salt in the database alongside the password. I would use something from the user that wouldn't change without a password change, as well as some type of semi-long data that also got hashed and put into the "pepper". Even if it's a file on disk that contains data that is read into memory and hashed with something that doesn't change (or at least can't change without the user also re-entering or creating a new password). Also, thank you for teaching me the term "pepper", because I feel like that is so relatable, but also different enough to correlate the two, but show how "pepper" is more powerful and useful!

Re: Okta Bcrypt incident lessons for designing better APIs

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

So let me take on the burden of stupid here: how are a password hash and a string-based KDF different? (I mean, the oldest well-known example of the former literally calls itself a PBKDF.) I understand this particular function from strings to large fixed numbers was limited in the length of the string it would accept, and I agree that’s a problem, but it feels like a problem orthogonal to the distinction you’re drawi…

A potential distinction is entropy preservation. For password hashes you usually want to preserve as much entropy as possible although one could argue that beyond 256 bits of output it may not matter (only one-time pads would suffer from smaller output). KDFs on the other hand must output a correctly-sized key for a particular cipher and so have further constraints on output choices (and potentially avoiding weak keys, e.g. for elliptic curve point generation).

Re: Okta Bcrypt incident lessons for designing better APIs

#54
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.

Re: Okta Bcrypt incident lessons for designing better APIs

#55
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.

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?

Re: Okta Bcrypt incident lessons for designing better APIs

#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?

Re: Okta Bcrypt incident lessons for designing better APIs

#57
I've seen this multiple times - even better I don't know how many ways we found a simple workaround or bypass of the complete process in so many apps... In essence this has nothing to do with the API itself but the way in which is another ballgame altogether. Great post though.

Re: Okta Bcrypt incident lessons for designing better APIs

#58
post #48

> On the other hand, such long usernames are not very usual, which I agree with Weird take. Usernames are often chosen by the user. Less so in corporate world but definitely not unheard of

Many of my usernames at my company are based on my email and it's pretty long - by the time you add the domain it's a good 47 characters...

Re: Okta Bcrypt incident lessons for designing better APIs

#59
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…

I would guess that they felt comfortable that the bcrypt output (192 bits) is enough that collisions are very unlikely. If these were already partitioned by customer, rather than being a single cache for the entire Okta userbase that seems fine. You're going to have weird cosmic ray bugs more often than a natural collision.

Now, the data structure they're using for a cache will use some sort of hash table, likely in memory, so maybe they've got the 192-bit bcrypt "key" and then that's hashed again, perhaps well or perhaps badly [e.g. C++ really likes using the identity function so hash(12345) = 12345] but then a modulo function is applied to find the key in an index and then we go groping about to look for the Key + Value pair. That part the API probably took care of, so even if the hash has size 6-bits, the full 192-bit key was checked. But the original data (userid:username:password) is not compared, only that 192-bit cache key.

Re: Okta Bcrypt incident lessons for designing better APIs

#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.

Post reply on HN