6 ms·
<< The Bcrypt algorithm was used to generate the cache key where we hash a combined string of userId + username + password. During specific conditions, this cou
by fanf2 2y ago
<< The Bcrypt algorithm was used to generate the cache key where we hash a combined string of userId + username + password. During specific conditions, this could allow users to authenticate by only providing the username with the stored cache key of a previous successful authentication. >>
https://man.openbsd.org/crypt https://man.openbsd.org/crypt
<< The maximum password length is 72. >>
So if the userid is 18 digits, the username is 52 characters, and the delimiters are 1 character each, then the total length of the non-secret prefix is 72, and bcrypt will drop the secret suffix.
You aren’t supposed to put more than the salt and the password into trad unix password hashes.
- marginalia_nu 2y ago> You aren’t supposed to put more than the salt and the password into trad unix password hashes. To be fair, they're basically salting with the userid and username. Still unorthodox to be sure.
- fanf2 2y agoThe salt is a separate input to the algorithm that is used differently and usually more restricted than the password.
- njtransit 2y agoThe goal of a salt is to prevent lookup attacks. Since the user id is unique to each user, it prevents the use of pre-computed lookup tables like a salt would.
- heliosyne 2y agoThe security of salting is twofold. Yes, it defeats the common rainbow table. But if the salt is known, a rainbow table for that salt can be computed. The security of salting depends on the salt being unknown. If the salt is externally known, which the username and userID necessarily are, then the rainbow table for that account can be computed entirely offline, defeating the point of salting.
- lmz 2y agoNo. Such a rainbow table is per-username and non reusable. Which is the point of salting.
- heliosyne 2y agoNo, rainbow tables are hash-input specific. They're user-specific only if the salt is user-unique. Usernames aren't normally part of the hash input because they're assumed-public knowledge. You can test this for yourself by creating a user account, then editing the master password database and manually changing the username without recalculating the password hash. The password will still work. If the username was part of the hash input, the password would fail.
- lmz 2y agoYou complained about them salting using the public username as salt. You asserted that this makes the rainbow table computable offline. I asserted that this didn't matter much for security since the table for H(username || secret) is username specific and not reusable for other usernames. Since precomputed rainbow tables consume quite a bit of space it is rather unlikely that anyone would have such a table stored for any random username.
- heliosyne 2y agoI didn't complain about anyone. The Okta vulnerability isn't because of public salts.
- vlovich123 2y agoYou can still build a rainbow table for a specific username if you want to do a targeted attack.
- MattPalmer1086 2y agoThe primary purpose of salting is to prevent precomputation being used to attack all users (e.g. rainbow tables). Even when specific salts are known they have already done this job. Salts are not intended to be secrets. If you want to treat a salt as if it was a private key, that would only provide additional protection for the very specific circumstance where the user hash is compromised, but the corresponding salt was not.
- marginalia_nu 2y agoThat's fair. Though you can salt a hash using a function that does not take a distinct salt input by just concatenating the salt with the value. This is a relatively common practice, but of course only works if there is no truncation of the salted input.
- 0x457 2y agoI mean yes overall, but why would put delimeters into hash? you just smash bytes together.
- jrockway 2y agoUsername: x@example.xyz Password: .com/!@#$% Concatenated: x@example.xyz.com/!@#$% Username: x@example.xyz.com Password: /!@#$% Concatenated: x@example.xyz.com/!@#$%
- Dylan16807 2y agoA delimiter fixes the problem if you're sure the delimiter character can never be inside the username and password. Better would be to prefix the length of each field. Better still would be separately hashing each field and concatenating the results.
- akira2501 2y agoI tend to use '\0' as a delimiter for this reason.
- Dylan16807 2y agoYou still need to make sure nulls can't show up, and you need to consider possible truncation scenarios caused by those nulls and make sure they won't cause silent failures at any point.
- akira2501 2y ago> You still need to make sure nulls can't show up Which is very easy to do without losing any desired functionality as opposed to delimiters in the ASCII character range. > and you need to consider possible truncation scenarios In particular hashing libraries worth using never have this problem. > and make sure they won't cause silent failures at any point. They literally only need to exist in the data to one function call. Afterwards they are not needed or significant.
- magicalhippo 2y agoPotentially ignorant question, why would they go for bcrypt over say HKDF[1], especially since they mix in public data like the username and potentially userid? [1]: https://datatracker.ietf.org/doc/html/rfc5869 https://datatracker.ietf.org/doc/html/rfc5869
- ronsor 2y agoWhy do we need a KDF for a cache key? Won't a normal cryptographic hash function (or its HMAC variant) suffice?
- fanf2 2y agoIf the cache gets leaked, you don’t want any miscreants to be able to bruteforce passwords from the cache keys.
- ronsor 2y agoDo we need to put the password in the cache key?
- magicalhippo 2y agoIf the user changes password it invalidates the cache entries automatically, so you avoid stale credentials exploiting the cache At least that's my immediate thought, could be wrong.
- ptcrash 2y agoIf you want to validate a username/password authn attempt against a cache, then yes the username and password have to be someone in the mix.
- sebastialonso 2y agoCan't believe the answers you're getting. The answer's a big fat NO. If you find yourself in that situation, there's something very incorrect with your design.
- a-dub 2y agowhy would someone in 2024 reach for bcrypt for building a secure hash key?
- EasyMark 2y agookta has been around longer than a year and momentum keeps a lot of companies from changing anything until catastrophe strikes
- pquerna 2y agoper <https://trust.okta.com/security-advisories/okta-ad-ldap-delegated-authentication-username/ https://trust.okta.com/security-advisories/okta-ad-ldap-dele...> 2024-07-23 - Vulnerability introduced as part of a standard Okta release This issue is not an "okta is old" issue. this was new code written in 2024 that used a password hashing function from 1999 as a cache key.
- crest 2y agoBcrypt is still perfectly usable for its original purpose. They just picked/wrote a bad implementation that silently truncated inputs longer than the maximum input length. Would you also ask why they picked AES (a cipher from 1998) when the error was with the user (e.g. picking fixed/too short key)?
- heliosyne 2y agoBecause bcrypt is still viable. Its cost factor is easily scaled to commodity performance, keeping the attack cost high. The main attack vector these days is GPU-based compute. There, SHA* algorithms are particularly weak because they can be so efficiently computed. Unlike SHA algorithms, bcrypt generates high memory contention, even on modern GPUs. Add in the constraint of broad support, low "honest use" cost, and maturity (extensive hostile cryptanalysis), bcrypt stays as one of the better choices even 25 year later. That said, bcrypt's main limitation is it has a low memory-size cost. There are some newer algorithms that improve on bcrypt by increasing the memory-size cost to more than is practical even for FPGA attacks. More importantly, bcrypt didn't actually fail here. The vulnerability happened because okta didn't use it correctly. All crypto is insecure if you use it wrong enough.
- gzer0 2y agoHere's how I see it: Core issue (okta's approach): * They concatenated userId + username + password for a cache key * Used BCrypt (which has a 72-byte limit) * The concatenation could exceed 72 bytes, causing the password portion to be truncated Why this is problematic: * BCrypt is designed for password hashing, not cache key generation * Mixing identifiers (userId, username) with secrets (password) in the same hash * Truncation risk due to BCrypt's limits Password storage should be separate from cache key generation. Use a random salt + appropriate hash function and for cache keys - use HMAC or KDF w/appropriate inputs
- 3np 2y agoThat should also mean that ca 50-52 character usernames are likely easily bruteforcable. Which makes the preconditions wider than those stated in the publication.