Uh oh!
There was an error while loading. Please reload this page.
Fix the implementation of BlowFish to solve bugs concerning NUL bytes truncating strings - #21675
Fix the implementation of BlowFish to solve bugs concerning NUL bytes truncating strings#21675LamentXU123 wants to merge 10 commits into
Conversation
Uh oh!
There was an error while loading. Please reload this page.
| zend_long cost = PHP_PASSWORD_BCRYPT_COST; | ||
| if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) { | ||
| if (zend_str_has_nul_byte(password)) { |
There was a problem hiding this comment.
nit: I also refactor this by the way.
alecpl
commented
Apr 9, 2026
Shouldn't that be rather done in |
LamentXU123
commented
Apr 9, 2026
It is before, see the initial fix in 6f14bee Actually I think that'd be better. Anyway, lets wait for code owners. |
Girgias
commented
Apr 9, 2026
Can we not fix the blowfish implementation? It seems like it requires providing the length of the string all the way down to |
Blowfish do support null bytes, but in php implementation it specifically doesn't. Any password with NUL will fail when hashing by using bugs with NUL bytes is inevitable since PHP is based on C string. But in this case, I think directly rejecting all inputs containing NUL bytes will be safer and always correct. |
Girgias
commented
Apr 10, 2026
Sure, but we "own" the implementation of BF, so why not actually fix the implementation? I'm happy to reject them as a bug fix that is backported, but in the long run we should fix the implementation. |
LamentXU123
commented
Apr 10, 2026
In the long run we should. But for now, password_hash does not accept password containing NUL, so fixing the implementation would create a BC break on this which requires a RFC. I would be happy to write such RFC in the future but I think this bug could be fix before we get it passed. |
Girgias
commented
Apr 10, 2026
I don't think fixing the implementation to be correct is a BC break. |
LamentXU123
commented
Apr 10, 2026
That'd be good. I will work on this hours later. I assume that the best practice here would be passing the correct length throughout the implementation so the strings will not be cut by NUL |
Girgias
commented
Apr 10, 2026
Correct :) |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| Z_PARAM_STRING(str, str_len) | ||
| Z_PARAM_STRING(salt_in, salt_in_len) |
There was a problem hiding this comment.
We may want these to be Z_PARAM_PATH if we use the system crypt. Or Maybe it should indeed always be PATH so that the behaviour is consistent with the system API...
There was a problem hiding this comment.
We may want these to be Z_PARAM_PATH
I implement the bcrypt() to reject NUL byte, and make password_* not to.
Not sure about this change.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Gina Peter Banyard <girgias@php.net>
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
This file is a vendored library and must not be touched.
TimWolla
commented
Apr 13, 2026
It's not BlowFish, it's Bcrypt. And no, most Bcrypt implementation don't support NULs. |
ndossche
commented
Apr 13, 2026
The idiotic thing about how PHP implements password hashing is that it doesn't always use the builtin bcrypt implementation: it can defer to crypt_r which doesn't support NUL bytes. So if you want to fix this you'll need to ditch that other code path but that runs into political issues about "bundling too much code". |
bukka
commented
Apr 13, 2026
I guess the idea was that crypt implementation would be more secure but not sure if there's much value in it. |
bukka
commented
Apr 13, 2026
I'm also not sure if there's much value in allowing nul bytes in password. I think the initial idea of not allowing in the verify was more reasonable. That was sort of the plan when we were handling GHSA-h746-cjrr-wfmr - prohibit the hashing as low security issue and add warning for verify in master. |
LamentXU123
commented
Apr 14, 2026
I don't have strong opinions on both solutions, but I would revert back to the initial fix I guess if we are following GHSA-h746-cjrr-wfmr plus: Is this issue worth a separate security entry? I mean that |
TimWolla
commented
Apr 14, 2026
Please explain the impact you're seeing. I'm seeing none and I don't think this change is necessary either. For |
I dont think it will break "working logic". As I have said, originally, password containing NUL doesn't work right in password_verify, and, it is rejected in password_hash. In this case, rejecting it is always correct. The impact is, we can avoid something like While not touching existing logic. I really dont think the above behavior is intentional. Password containing NUL byte in original will cause ambiguous verification, so the original code doesn't treat it correctly. |
Sjord
commented
Aug 22, 2026
Looks good to me. I can imagine someone creating bcrypt hashes of strings with null bytes outside of PHP, and then verifying them with However, this will be pretty rare. Users don't typically put null bytes in their password. bcrypt, |
Now, since including \0 in
password_hashis not allowed in PASSWORD_BCRYPT. It is reasonable to add checkers that, makes input that includes \0 inpassword_verifydirectly fails.This PR align
password_verify()withpassword_hash()for bcrypt by rejecting passwords containing NUL bytes before verification.Because bcrypt treats passwords as NUL-terminated C strings, this avoids ambiguous verification behavior for inputs such as "foo\0bar". A regression test is included.
Fix#21673
To be short: password encrypted by BYCRYPT should never includes \0. So directly rejecting it is always correct. It also avoids ambiguous verification where something like this happens: