#Bun.password.verify does not work with bCrypt
1 messages ยท Page 1 of 1 (latest)
bun is supposed to automatically detect the hash type from the content, so passing in "bcrypt" as the third argument does nothing
I'll see if I can reproduce on windows and linux tomorrow
tried without passing bcrypt parameter and didn't work either
that's why I tried passing it and it still doesn't work
Thanks
$ bun run bcrypt_hash.ts
Enter Password: some password
Read: some password
Hash: $2b$10$ut/yHDE/4j67EzTM8X7dc.Po3/V05M4zf2latauaLaU8MiGxsWH6y
Verify result: true
$ cat bcrypt_hash.ts
const pass = prompt("Enter Password:");
console.log(`Read: ${pass}`);
const hash = Bun.password.hashSync(pass, { algorithm: 'bcrypt', cost: 10 });
console.log(`Hash: ${hash}`);
console.log(`Verify result: ${Bun.password.verifySync(pass, hash)}`);
A similar program seems to work fine on linux
I'll try the async version of the hash functions
$ bun run bcrypt_hash_async.ts
Enter Password: another password
Read: another password
Hash: $2b$10$Y8upaJsW6S0NZCAfzlTqq.jFZUYdqrzcVmUCPlzZLGebzdX37WuNO
Verify result: true
Still failed to reproduce on linux
Interesting results on windows
$ bun run bcrypt_hash.ts
Enter Password: some password
Read: some password
Testing Sync
Hash: $2b$10$e7GoVjhSR6NchzXOswVJFuM2KBKYBXHjUhXVRUzucyroHckirEqbu
Verify result: true
Testing Async
Hash: $2b$10$t4X4HomGuokucmkdlE/Ire.vmq2xLqtlFxenz.QarU5CyQHU4K4by
Verify result: true
Hash from sync and async were equal: false
const pass = prompt("Enter Password:");
console.log(`Read: ${pass}`);
console.log(`Testing Sync`);
const hash = Bun.password.hashSync(pass, { algorithm: 'bcrypt', cost: 10 });
console.log(`Hash: ${hash}`);
console.log(`Verify result: ${Bun.password.verifySync(pass, hash)}`);
console.log(`Testing Async`);
const hash2 = await Bun.password.hash(pass, { algorithm: 'bcrypt', cost: 10 });
console.log(`Hash: ${hash2}`);
console.log(`Verify result: ${await Bun.password.verify(pass, hash2)}`);
console.log(`Hash from sync and async were equal: ${hash == hash2}`);
The hashes from sync and async were different, but both verified correctly
it seems that bun's password hashing automatically adds a salt value - this difference in hash when run multiple times also happens on linux
that doesn't explain why verify returned false in your case though
it seems to fail for passwords longer than 72 characters
but that bug should be pretty easy to fix
hmm, 72 is fine here, but 73 is not.
(Linux)
maybe prompt is returning \r but dropping the \n?
oh yeah it might
printing .length does say there's an extra character
so yeah 72 is fine, 73 is not
that matches the code, which does an sha512 if password.length > 72
yep, I've fixed it locally and will open a PR
๐
test("bcrypt longer than 72 characters is the SHA-512", async () => {
const boop = Buffer.from("hey".repeat(100));
const hashed = await password.hash(boop, "bcrypt");
expect(await password.verify(Bun.SHA512.hash(boop), hashed, "bcrypt")).toBeTrue();
});
that doesn't make much sense to me
so it seems you guys were able to reproduce the problem?
oh, sorry, missed that
Bcrypt supposedly has a 72 char length limit so passwords beyond that size are sha256d so they are 64 chars
sha512 (and used as raw input), but yeah ๐
Oh you figured it out nice
yeah, just not sure whether it's already too late to break compatibility ๐
I think it makes sense to have verify be able to verify hashed passwords
I think it's certainly too much to expect of the user to count bytes (not characters), compare to 72, and use a different function in that case, but if people are already doing it...
It would probably have been reported by then
Yeah Blake2b generated hash
So should I just use Argon2id?
But this should probably be fixed at least for verify
I need to figure out how to print a deprecation warning for "bcrypt" when no strategy is specified for passwords > 72 bytes, that seems best to me.
the one that uses bcrypt for Bun.password.hash and Bun.password.verify, with passwords longer than 72 bytes wouldn't work for them either, so I'm not sure if deprecation warning is even needed here
Just a fix
or will hashes also be different for passwords smaller than 72 bytes and verify wouldn't work anymore for them after the fix?
can you try now? the simple fix as you describe it was merged
Will check thanks
Personally I prefer Argon2id over bcrypt as well, but for self-hosters, it would be a lot easier to choose the cost for bcrypt as it only includes one parameter that needs to be changed
and if you increase it, it would take longer to hash
so you can as well make a simple function that loops thru cost and use the one that needs at least 350ms to hash as an example
so the "best" cost would be used automatically depending on the server performance
with Argon2id there is just too much parameters
and it would confuse everyone
also parallelism is not supported in Argon2id?
or hash length
for front-end, I have compiled a WASM file from Argon2id Rust implementation and use WebWorkers for parallelism. https://argon2id.rabbit-company.com/
but on back-end with Bun I can't increase parallelism or change hash length
doesn't argon2id Just Work with the default configuration?
for example if I want to generate hash on FE and BE
(why is the back end hashing passwords at all?)
and verify on both sides
oh, verify ๐
yeah
this could also reduce server side load and make client suffer with hashing
client resources are free anyways
also by hashing on FE and sending hashed password on BE, this would prevent server ever knowing user password in plain text
as most users use 1 password for everything
as I said, crypto is subtle and it's easy to get wrong. I'm not sure what I think about the whole idea of expensive password hashes
what do you usually use to hash user passwords?
IMHO it's probably better to use deliberately cheap hashes, then run a large dictionary against all substrings of the password to find out whether it's weak
I've not had to make that decision for a live site in ages. I honestly would excuse myself to do more research to see whether my ideas still make sense ๐
both bcrypt as well as argon2id includes salt, so this would prevent dictionary attacks.
well, it makes dictionary attacks more expensive, yes.
yeah they would need to hash every try again
argon is what I use for disk encryption passwords, but that's a different scenario since any attacker can get an encrypted disk image.
I have actually never used Argon2id for server-side password hashing. Only bcrypt.
but I'm using Argon2id for a password manager
I also think it would make sense to automatically try typo-fixing passwords that don't match, as it's a lot easier to type a seventy-character password with one or two typos than to type a 30-character password correctly consistently...
I usually just make a requirement for password entropy to score at least 75. https://passwordentropy.rabbit-company.com
I'm not checking if their password is already located in any password dictionary...
so basically as long as their password is brute-force "resistant" it is good.
my current impression is expensive hashing only helps with medium-strength passwords. weak passwords are weak, strong passwords are strong even with cheap hashing
so are you using something like sha256, sha512... for cheap password hashing?
or even cheaper algo
nothing cheaper than sha512 ๐
cool
I think at this point I have a Pavlovian reaction to even typing the characters "m", "d", "5"
I usually use sha512 for bearer tokens
anyway, if your default settings for argon2id aren't good or can't be changed as easily as they should be, please file an issue! It's important not to fall behind the security users are expecting, even if they're wrong to do so ๐
not long time ago, I have been migrating a PHP website from version 5 to 8. The company did used md5 for password hashing 
thanks will do
soon it'll all be replaced by passkeys, whether we want that or not.
It would probably take another decade or more
as it is a lot faster for each programmer to implement passwords than passkeys
This is the best way in my opinion:
- Something only you know (passwords)
- Something only you have (Yubikey, fingerprint, mobile phone?...)
With passkeys people want to switch from (Something only you know - passwords) to (Something only you have - passkeys)