#4 đ„ Leverage built-in authentication with #Breeze, #Fortify or #Jetstream
đïž Store passwords securely using #Bcrypt or #Argon2 hashing algorithms
đ Secure environment variables and force #HTTPS in production environments
@thinkberg this page is gold. Pitty that the #bcrypt one doesn't have a reference
Post-Quantum Cryptography Comes to Windows Insiders and Linux.
buff.ly/Ne4S3Lz
#win32 #windows #ncrypt #bcrypt #cryptography #encryption #linux
Post-Quantum Cryptography Come...
Post-Quantum Cryptography Comes to Windows Insiders and Linux.
#win32 #windows #ncrypt #bcrypt #cryptography #encryption #linux
@jadi This "#OpenBSD is secure!" claim always annoyed me a lot, mainly because it doesn't tell anything: #Security in IT can only ever be defined in a context of #threat models. Without that, it's meaningless. Somewhat recently, I discovered this:
I should warn it uses some sarcasm and other confrontative language in some parts, unfortunately. But it seems to be a pretty professional analysis and assessment of (mostly) the "mitigations" OpenBSD provides in an attempt to counter "typical" attacks by at least making them harder.
I should also add that I consider this a very interesting and helpful read, and still consider OpenBSD a great project that came up with lots of great stuff (I recently used their #bcrypt code after doing some research on password hashing, for example). And I don't agree with every single criticism on that page either. I just think it's important to build assessments whether something "is secure" on a serious analytical foundation.
This is... interesting. Apparently bcrypt truncates user provided passwords at 72 byte marker. I guess one way can be to "prehash" the password with a HMAC as suggested here:
https://soatok.blog/2024/11/27/beyond-bcrypt/
The other (simpler) approach would be to, like Go's x/crypto/bcrypt, just reject all user provided passwords > 72 bytes. It is not *great*, but it works and fails "safe". Now one wonders *why* this is not the default behavior of PHP's password_hash function...
@lcheylus That's where I pulled from. Still took quite a while.
So, now, this looks kind of messy, but I *think* I can build #OpenBSD's unmodified #bcrypt code on several (many?) systems wrapping it like this đ
https://github.com/Zirias/swad/blob/master/src/lib/swadbcrypt/bcrypt.c
I need some advise: Is there a good portable and free (really free, not GPL!) #implementation of #bcrypt in #C around?
There's #OpenBSD source I could use, but integrating that would probably be quite a hassle...
Background: I want to start creating a second credential checker for #swad using files. And it probably makes sense to support a sane subset of #Apache's #htpasswd format here. Looking at the docs:
https://httpd.apache.org/docs/current/misc/password_encryptions.html
... the "sane subset" seems to be just bcrypt. *MAYBE* also this apache-specific flavor of "iterated" MD5, although that sounds a bit fishy ...
New version of #hashgen published.
Changelog:
v1.1.0; 2025-03-19
added modes: #base58, #argon2id, #bcrypt w/custom cost factor
https://forum.hashpwn.net/post/89
#hashgenerator #hashcracking #hashcat #hashpwn #cyclone #golang
"Hallo, ich habe mein Passwort vergessen. Könnt ihr mir mein altes zuschicken?"
Nein. Und das ist auch gut so. đ
âThe #bcrypt password hashing function should only be used for password storage in legacy systems where #Argon2 and scrypt are not available.â
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#bcrypt #security #owasp
"What Okta Bcrypt incident can teach us about designing better APIs"
https://n0rdy.foo/posts/20250121/okta-bcrypt-lessons-for-better-apis/
What #Okta #Bcrypt incident can teach us about designing better #APIs
https://itnext.io/what-okta-bcrypt-incident-can-teach-us-about-designing-better-apis-b87efe2bb830
Beyond Bcrypt
In 2010, Coda Hale wrote How To Safely Store A Password which began with the repeated phrase, âUse bcryptâ, where the word bcrypt was linked to a different implementation for various programming languages.
This had two effects on the technology blogosphere at the time:
At the time, it was great advice!
Credit: CMYKatIn 2010, bcrypt was the only clearly good answer for password hashing in most programming languages.
In the intervening almost fifteen years, weâve learned a lot more about passwords, password cracking, authentication mechanism beyond passwords, and password-based cryptography.
If you havenât already read my previous post about password-based cryptography, you may want to give that one a once-over before you continue.
But weâve also learned a lot more about bcrypt, its limitations, the various footguns involved with using it in practice, and even some cool shit you can build with it.
In light of a recent discussion about switching WordPressâs password hashing algorithm from PHPass (which is based on MD5) to bcrypt, I feel now is the perfect time to dive into this algorithm and its implications on real-world cryptography.
Understanding Bcrypt in 2024
Bcrypt is a password hashing function, but not a password KDF or general-purpose cryptographic hash function.
If youâre using a sane password storage API, such as PHPâs password API, you donât even need to think about salting your passwords, securely verifying passwords, or handling weird error conditions. Instead, you only need concern yourself with the âcostâ factor, which exponentially increases the runtime of the algorithm.
Thereâs just one problem: bcrypt silently truncates after 72 characters (or rather, bytes, if youâre pedantic and assume non-ASCII passwords, such as emoji).
Hereâs a quick script you can run yourself to test this:
<?php$example1 = str_repeat('A', 72);$example2 = $example1 . 'B';$hash = password_hash($example1, PASSWORD_BCRYPT);var_dump(password_verify($example2, $hash));
This may sound ludicrous (âwho uses 72 character passwords anyway?â) until you see security advisories like this recent one from Okta.
The Bcrypt algorithm was used to generate the cache key where we hash a combined string of userId + username + password. Under a specific set of conditions, listed below, this could allow users to authenticate by providing the username with the stored cache key of a previous successful authentication.
(âŠ)
- The username is 52 characters or longer
The other thing to consider is that many people use passphrases, such as those generated from Diceware, which produce longer strings with less entropy per character.
If you use bcrypt as-is, you will inevitably run into this truncation at some point.
âLetâs pre-hash passwords!â
In response to this limitation, many developers will suggest pre-hashing the password with a general purpose cryptographic hash function, such as SHA-256.
And so, in pursuit of a way to avoid one footgun, developers introduced two more.
AJTruncation on NUL Bytes
If you use the raw binary output of a hash function as your password hash, be aware that bcrypt will truncate on NUL (0x00
) bytes.
With respect to the WordPress issue linked above, the default for PHPâs hashing API is to output hexadecimal characters.
This is a bit wasteful. Base64 is preferable, although any isomorphism of the raw hash output that doesnât include a 0x00
byte is safe from truncation.
Hash Shucking
When a system performs a migration from a cryptographic hash function (e.g., MD5) to bcrypt, they typically choose to re-hash the existing hash with bcrypt.
Because users typically reuse passwords, you can often take the fast, unsalted hashes from another breach and use it as your password dictionary for bcrypt.
If then you succeed in verifying the bcrypt password for a fast hash, you can then plug the fast hash into software like Hashcat, and then crack the actual password at a much faster rate (tens of billions of candidates/second, versus thousands per second).
This technique is called hash shucking (YouTube link).
You can avoid hash shucking by using HMAC with a static keyâeither universal for all deployments of your software, or unique per application.
It doesnât really matter which you choose; all you really need from it is domain separation from naked hashes.
I frequently see this referred to as âpepperingâ, but the term âpepperâ isnât rigidly defined anywhere.
One benefit of using a per-application HMAC secret does make your hashes harder to crack if you donât know this secret.
For balance, one downside is that your hashes are no longer portable across applications without managing this static key.
Disarming Bcryptâs Footguns
Altogether, itâs quite straightforward to avoid bcryptâs footguns, as I had recommended to WordPress last week.
Easy, straightforward, and uncontroversial. Right?
Objections to Bcrypt Disarmament
The linked discussion was tedious, so I will briefly describe the objections raised to my suggestion.
When you develop a popular CMS, library, or framework, you cannot possibly know all the ways that your software will be used by others. Itâs almost always better to be misuse-resistant.
$2w$
) to distinguish disarmed bcrypt from vanilla bcrypt that PHPâs password API wouldnât know what to do with.There were some other weird arguments (such as âBcrypt is approved by NIST for FIPSâ, which is just plain false).
Why Bcrypt Truncating SHA-512 Doesnât Matter
If you have a random secret key, HMAC-SHA-512 is a secure pseudorandom function that you can treat as a Random Oracle.
Because itâs HMAC, you donât have to worry about Length Extension Attacks at all. Therefore, the best known attack strategy is to produce a collision.
The raw binary output of SHA-512 is 64 characters, but may contain NUL characters (which would truncate the hash). To avoid this, we base64-encode the output.
When you base64-encode a SHA-512 hash, the output is 88 characters (due to base64 padding). This is longer than the 72 characters supported by bcrypt, so it will truncate silently after 72 characters.
This is still secure, but to prove this, I need to use math.
First, letâs assume youâre working with an extremely secure, high-entropy password, and might be negatively impacted by this truncation. How bad is the damage in this extreme case?
There are 64 possible characters in the base64 alphabet. Thatâs tautology, after all.
If you have a string of length 72, for which each character can be one of 64 values, you can represent the total probability space of possible strings as .
If you know that , you can do a little bit of arithmetic and discover this quantity equal to .
As I discussed in my deep dive on the birthday bound, you can take the cube root of this number to find what I call the Optimal Birthday Bound.
This works out to samples in order to find a probability of a single collision.
This simply isnât going to happen in our lifetimes.
2^-144 is about 17 trillion times less likely than 2^-100.The real concern is the entropy of the actual password, not losing a few bits from a truncated hash.
After all, even though the outputs of HMAC-SHA512 are indistinguishable from random when you donât know the HMAC key, the input selection is entirely based on the (probably relatively easy-to-guess) password.
âWhy not just use Argon2 or Scrypt?â
Argon2 and scrypt donât have the bcrypt footguns. You can hash passwords of arbitrary length and not care about NUL characters. Theyâre great algorithms.
Several people involved in the Password Hashing Competition (that selected Argon2 as its winner) have since lamented the emphasis on memory-hardness over cache-hardness. Cache-hardness is more important for short run-times (i.e., password-based authentication), while memory-hardness is more important for longer run-times (i.e., key derivation).
As Sc00bz explains in the GitHub readme for his bscrypt project:
Cache hard algorithms are better than memory hard algorithms at shorter run times. Basically cache hard algorithms forces GPUs to use 1/4 to 1/16 of the memory bandwidth because of the large bus width (commonly 256 to 1024 bits). Another way to look at it is memory transactions vs bandwidth. Also the low latency of L2 cache on CPUs and the 8 parallel look ups letâs us make a lot of random reads. With memory hard algorithms, there is a point where doubling the memory quarters a GPU attackerâs speed. There then is a point at which a memory hard algorithm will overtake a cache hard algorithm. Cache hard algorithms donât care that GPUs will get ~100% utilization of memory transactions because itâs already very limiting.
Ironically, bcrypt is cache-hard, while scrypt and the flavors of Argon2 that most people use are not.
Most of my peers just care that you use a password hashing algorithm at all. They donât particularly care which. The bigger, and more common, vulnerability is not using one of them in the first place.
Iâm mostly in agreement with them, but I would prefer that anyone that chooses bcrypt takes steps to disarm its footguns.
Turning Bcrypt Into a KDF
Earlier, I noted that bcrypt is not a password KDF. That doesnât mean you canât make one out of bcrypt. Ryan Castellucci is an amazing hacker; they managed to do just that.
To understand why this is difficult, and why Ryanâs hack works, you need to understand what bcrypt actually is.
Bcrypt is a relatively simple algorithm at its heart:
"OrpheanBeholderScryDoubt"
64 times in ECB mode using the expanded key from step 1.Most of the heavy work in bcrypt is actually done in the key schedule; the encryption of three blocks (remember, Blowfish is a 64-bit block cipher) just ensures you need the correct resultant key from the key schedule.
âSo how do you get an encryption key out of bcrypt?â
Itâs simple: we, uh, hash the S-box.
static void BF_kwk(struct BF_data *data, uint8_t kwk[BLAKE2B_KEYBYTES]) { BF_word *S = (BF_word *)data->ctx.S; BF_htobe(S, 4*256); // it should not be possible for this to fail... int ret = blake2b_simple(kwk, BLAKE2B_KEYBYTES, S, sizeof(BF_word)*4*256); assert(ret == 0); BF_betoh(S, 4*256);}
Using BLAKE2b to hash the S-box from the final Blowfish key expansion yields a key-wrapping key that can be used to encrypt whatever data is being protected.
The only feasible way to recover this key is to provide the correct password and salt to arrive at the same key schedule.
Any attack against the selection of S implies a cryptographic weakness in bcrypt, too. (Iâve already recommended domain separation in a GitHub issue.)
CMYKatItâs worth remembering that Ryanâs design is a proof-of-concept, not a peer-reviewed design ready for production. Still, itâs a cool hack.
Itâs also not the first of its kind (thanks, Damien Miller).
If anyone was actually considering using this design, first, they should wait until itâs been adequately studied. Do not pass Go, do not collect $200.
Additionally, the output of the BLAKE2b hash should be used as the input keying material for, e.g., HKDF. This lets you split the password-based key into multiple application-specific sub-keys without running the password KDF again for each derived key.
Wrapping Up
Although bcrypt is still an excellent cache-hard password hashing function suitable for interactive logins, it does have corner cases that sometimes cause vulnerabilities in applications that misuse it.
If youâre going to use bcrypt, make sure you use bcrypt in line with my recommendations to WordPress: HMAC-SHA-512, base64 encode, then bcrypt.
Hereâs a quick proof-of-concept for PHP software:
<?phpdeclare(strict_types=1);class SafeBcryptWrapperPoC{ private $staticKey; private $cost = 12; public function __construct( #[\SensitiveParameter] string $staticKey, int $cost = 12 ) { $this->staticKey = $staticKey; $this->cost = $cost; } /** * Generate password hashes here */ public function hash( #[\SensitiveParameter] string $password ): string { return \password_hash( $this->prehash($password), PASSWORD_BCRYPT, ['cost' => $this->cost] ); } /** * Verify password here */ public function verify( #[\SensitiveParameter] string $password, #[\SensitiveParameter] string $hash ): bool { return \password_verify( $this->prehash($password), $hash ); } /** * Pre-hashing with HMAC-SHA-512 here * * Note that this prefers the libsodium base64 code, since * it's implemented in constant-time */ private function prehash( #[\SensitiveParameter] string $password ): string { return \sodium_bin2base64( \hash_hmac('sha512', $password, $this->staticKey, true), \SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING ); }}
You can see a modified version of this proof-of-concept on 3v4l, which includes the same demo from the top of this blog post to demonstrate the 72-character truncation bug.
If youâre already using bcrypt in production, you should be cautious with adding this pre-hashing alternative. Having vanilla bcrypt and non-vanilla bcrypt side-by-side could introduce problems that need to be thoroughly considered.
I can safely recommend it to WordPress because they werenât using bcrypt before. Most of the people reading this are probably not working on the WordPress core.
Addendum (2024-11-28)
More of the WordPress team has chimed in to signal support for vanilla bcrypt, rather than disarming the bcrypt footgun.
The reason?
That would result in maximum compatibility for existing WordPress users who use the Password hashes outside of WordPress, while also not introducing yet-another-custom-hash into the web where itâs not overly obviously necessary, but while still gaining the bcrypt advantages for where itâs possible.
The hesitance to introduce a custom hash construction is understandable, but the goal I emphasized with bold text is weird and not a reasonable goal for any password storage system.
Itâs true that the overwhelming non-WordPress code written in PHP is just using the password hashing API. But that means they arenât compatible with WordPress today. PHPâs password hashing API doesnât implement phpass, after all.
In addition to being scope creep for a secure password storage strategy, itâs kind of a bonkers design constraint to expect password hashes be portable. Why are you intentionally exposing hashes unnecessarily?
CMYKatAt this point, itâs overwhelmingly likely that WordPress will choose to not disarm the bcrypt footguns, and will just ship it.
Thatâs certainly not the worst outcome, but I do object to arriving there for stupid reasons, and that GitHub thread is full of stupid reasons and misinformation.
The most potent source of misinformation also barked orders at me and then tried to dismiss my technical arguments as the concerns of âthe hobbyist communityâ, which was a great addition to my LinkedIn profile.
If WordPressâs choice turns out to be a mistakeâthat is to say, that their decision for vanilla bcrypt introduces a vulnerability in a plugin or theme that uses their password hashing API for, I dunno, API keys?âat least I can say I tried.
Additionally, WordPress cannot say they didnât know the risk existed, especially in a courtroom, since me informing them of it is so thoroughly documented (and archived).
CMYKatHereâs to hoping the risk never actually manifests. Saying âI told you soâ is more bitter than sweet in security. Happy Thanksgiving.
Header image: Art by Jim and CMYKat; a collage of some DEFCON photos, as well as Creative Commons photos of Bruce Schneier (inventor of the Blowfish block cipher) and Niels Provos (co-designer of bcrypt, which is based on Blowfish).
#bcrypt #cryptography #passwordHashing #passwords #SecurityGuidance
How To Make A WireGuard Easy (wg-easy) VPN Server With Web-Based Admin UI On An Ubuntu Linux VPS https://youtu.be/i-ezlKq7V54 #Websplaining #WireGuard #wgeasy #WireGuardEasy #VPN #WireGuardVPN #VirtualPrivateNetwork #VPS #VirtualPrivateServer #Docker #CloudServer #Ubuntu #Linux #UbuntuLinux #Bcrypt #PasswordHash #PasswordHasher #GeneratePasswordHash #WebUI #WebBasedUI #UserInterface #AdminUI #UI #Server
Seeing a lot of commentary on Okta's long-username vuln, stating that the reason is that bcrypt ignores input above a certain length.
What seems to be missing is commentary on how absolutely fricking bonkers it is that a "secure" hashing algorithm ignores input!?
How common is it that implementations error out vs silently ignore the input? Did Okta ignore an error condition? Seems unlikely to fail open if a password hash errors out... but does that mean there are implementations of a hashing algorithm commonly used for this sort of thing, floating around, silently truncating its input?
Granted, 50-70 bytes is "enough for anyone" in terms of password entropy, but as per Okta*s advisory, all it takes is for someone to concatenate something. Yuck.