ircmaxell / password_compat Goto Github PK
View Code? Open in Web Editor NEWCompatibility with the password_* functions that ship with PHP 5.5
License: MIT License
Compatibility with the password_* functions that ship with PHP 5.5
License: MIT License
public function login(UserModel $userModel)
{
try{
$name = $userModel->get_user_user_name();
$password = $userModel->get_user_password();
$query = "SELECT * FROM users WHERE (user_user_name = :user_user_name) AND (user_status_id = 5) AND ((date_expiry > NOW()) OR (date_expiry < :date_expiry))";
$stmt = $this->query($query);
$this->bind(":user_user_name", $name);
$this->bind(":date_expiry", '2000-01-01');
$stmt = $this->executer();
$res = $stmt->fetch(PDO::FETCH_ASSOC);
//echo $password." string";
//var_dump(password_verify($password, $res["user_password"]));
if(password_verify($password, $res["user_password"])){
$_SESSION['user_id'] = $res['user_id'];
$this->user_id = $res["user_id"];
$this->user_name = $res['user_name'];
$this->is_authenticated = TRUE;
$this->expiry_date = $res['expiry_date'];
$this->session_start_time = time();
$this->create_session();
}else {
echo "password don not match";
}
}catch(PDOException $e){
echo $e->getMessage();
return FALSE;
}
return TRUE;
}
THATS MY LOGIN FUNCTION.
---------------------------------------------------------------------------------->
public function add_user(UserModel $userModel)
{
try {
$pass = $userModel->get_user_password();
$name = $userModel->get_user_name();
$user_name = $userModel->get_user_user_name();
$phone_number = $userModel->get_user_phone_number();
$email = $userModel->get_user_email();
$school_id = $userModel->get_user_school_id();
$hash_pass = password_hash($pass, PASSWORD_DEFAULT);
var_dump($hash_pass);
$sql = "INSERT INTO users (user_name, user_user_name, user_password, user_phone_number, user_email, user_school_id) VALUES
(:user_name, :user_user_name, :user_password, :user_phone_number, :user_email, :user_school_id)";
$stmt = $this->query($sql);
$this->bind(":user_name", $name);
$this->bind(":user_user_name", $user_name);
$this->bind(":user_password", $hash_pass);
$this->bind(":user_phone_number", $phone_number);
$this->bind(":user_email", $email);
$this->bind(":user_school_id", $school_id);
$stmt = $this->executer();
} catch (Exception $e) {
echo $e->getMessage();
return FALSE;
}
return TRUE;
}
MY ISSUE IS THAT ITS NOT LOGIN A USER IN.... AND THE ISSUE IS FROM THE PASSWORD_VERIFY AND PASSWORD_HASH FUNFCTION... PLEASE HELP....
I generated a 6 digit passcode for a users password using mt_rand this returned an int, when it comes to hashing the int it errors.
https://github.com/ircmaxell/password_compat/blob/master/lib/password.php#L31
Why is null
or a boolean a valid salt? Sure, it will be caught later by the length check, but that's only because those values happen to have a string representation with less than 22 characters. Semantically, this makes no sense.
Why is a number a valid salt? 22 decimal digits are hardly acceptable.
Is it really a good idea to accept objects? Just because it has a __toString()
method doesn't mean it was actually intended to mimic a string. This is very error-prone, because the user might accidentally pass some object which merely contains the salt string (an instance of a randomness class or whatever).
In my opinion, the function should only accept a real, actual string. This might be against “the PHP way”, but I don't think a security-related function is the right place for type juggling, duck typing or whatever.
I've been using this library for a while, but I encountered issues with my login system today. After extensive debugging including at least four code refactors, I've finally narrowed the issue down to (unfortunately) this library. I downloaded this copy on the 26th of November, and the basic issue is that the hash being returned for a predefined password varies between page loads. For instance, I am currently hashing a simple string, "admin". A sample hash is "$2y$12$3M46vmkpRtgvanC.ViCBJuXnIieGTqWHG7csDdsOKbJHDFlNCQ7kW", followed by "$2y$12$QsgXA6f1itohqd5YA0XhI.18EjF5B45RKwRFSD4Eic3M9vf3FtpDO" on the next load. This is the code I am using:
password_hash($pass, PASSWORD_BCRYPT, array("cost" => 12));
For reference, when I use a plain md5()
hash, it returns a constant, expected value of "21232f297a57a5a743894a0e4a801fc3".
I am using PHP 5.4.7 on a Windows 7 installation (WAMPServer).
I know that this will be extra processing time, but my suggestion is to merge the version test into the main lib file, and if it fails the test, throw an exception. I'm assuming the main reason the password_* functions exist in the first place is to prevent people who don't know what they're doing from screwing things up, and I think that this would prevent it further. Since the hash uses a "04" cost parameter, verifying the hash is extremely fast (it took me 1.238 seconds for 1000 iterations with a relatively crappy CPU), so even on really slow hardware I can't see it taking longer than 5 ms per iteration.
Now that php 5.5 is out and considered stable and all, the tagline for the repository and the first line of the readme are a little misleading in that password_* is available in core now.
Hi,
My site is currently installed on a server with PHP 5.4.16 and I want to start hashing passwords on a site there.
In the future I will migrate to a server with later version of PHP and will be able to use the standard password_hash methods
Will the verification of older values work on the newer version? Or should I stay with this solution in later versions of PHP as well?
What is the recommended practice?
Thanks!
I know it's probably too late now, but using a separate value, such as 0
for PASSWORD_DEFAULT
would have been a wise idea. That way password_hash
could reject custom parameters when PASSWORD_DEFAULT
is used.
Combining PASSWORD_DEFAULT
with parameters such as cost
seems nonsensical, since the interpretation of the parameters depends on the algorithm. For example a cost of 11
might have a completely different meaning for BCrypt than for SCrypt.
Or do you have a better idea to prevent such issues?
If it makes sense, I suggest to consider to use E_USER_ERROR for the followings 2 lines instead of warning.
https://github.com/ircmaxell/password_compat/blob/master/lib/password.php#L37
https://github.com/ircmaxell/password_compat/blob/master/lib/password.php#L235
I submitted PR #87
In lib/password.php
line 23, the $options miss an "array" type hint (as in password_needs_rehash)
line 170, "password_create" is mentionned, but that should be "password_verify"
@ircmaxell For latest 'stable' version for product environment, should I download 'dev-master' or v1.0.4 as of today?
The documentation says that the hash of password_hash
should be passed as hash
parameter to password_verify
. It does not say anything about the result when this value contains something else.
I store the result of password_hash
in a database and check it against a password later on (as documented). But if the password is not initialized, an empty value is passed to password_verify
. The verification fails as crypt()
makes up its own salt, but that was for this particular case.
Are there any guarantees on the validation result when the hash is not exactly a return value of password_hash
? (note that the password argument can be anything as this is passed by the user)
I have an issue with generated hashes from PHP and this compat lib when I want to use the hash to check a password in Java or a nodejs backend.
$pass = password_hash("test", PASSWORD_DEFAULT);
// $pass = "$2y$10$9LIvdTW1CO8Nxy9Zc8l.eOaGI1hGFbW63u.CBYorwvrJQtVTEazzy"
When I want to test the password "test" against this hash, then I get "invalid salt revision". I get the same result with this online tool: https://www.dailycred.com/blog/12/bcrypt-calculator
Btw. my PHP version on my Mac is 5.4.17.
The error message in PHP core function is the same:
password_hash() expects at least 2 parameters, 0 given
In the backported function, there is something like this:
Missing argument 1....
This is not a major one, but still incompatible.
What I've done in backported function is that required arguments got a null default value (since type check is still necessary), so that I could trigger a proper error for less than enough argument count. The idea originates from ramsey/array_column.
Please add a 1.0.0-beta1 tag (or just a 1.0.0 tag) so I can be sure not to receive breaking changes when I require it in Composer.
Hello,
i accidentally happened to check hashed with password_verify() some strings starting with:
$2y$27$
$2y$31$
The function call takes long time to run starting from string beginning $2y$16$ - $2y$17$ up to $2y$31$. with $2y$27$ the script timeout is reached.
(it also happens for string just identical to the previous ones)
the same calls using php5.5 password_verify quickly return false.
kind regards
PS
my php ver 5.3.29
Doesn't work on PHP 5.6.98, but does on 5.4.25.
Have ran the test script and came back with Pass. Have checked using password_get_info and both servers return the same information, so at a loss as why this doesn't work. It's not the code as I know it's working because it does on the 5.4.25 server.
Any suggestions, really need to get this working?
Hello,
I just implemented this excellent library (thanks for creating it) a few days ago.
Now I got a report from a developer using PHP 5.3.8-ZS5.5.0 (cli) - which is perfectly matching the requirements - that password_hash returns false instead of a hash.
Seems like crypt() function reported a failure, but I have no clue how to find out why.
Testing the same against PHP 5.3.14 works without any problems.
Any idea ?
Hello,
is there an equivalent solution for php 5.2 ?
I know there is a solution for php 5.3.3
I use a recent version but not my users
:)
Best regards
When a user doesn't exist in my system or an empty password is entered I still want to validate the password, even though I know it's going to fail.
I now use password_verify(null, '$2y$' . PASSWORD_COST . '$');
, but validating this doesn't take as long as a valid hash does.
How can I fake validation that'll take as long as a valid one?
While the library does make sure that the return value of crypt()
is greater than 13 to catch the error strings and prevent a fallback to DES-crypt, it fails to recognize several other problems:
crypt()
may fall back to MD5-crypt instead of DES-crypt. In that case, the return value consists of 34 characters. This also won't be caught by the password_verify()
function, so the user might not even recognize the problem.$
, which leads to an invalid hash. This depends on the PHP version (see PHP bug 62488).The only way to catch all those problems is to carefully validate the return value:
mysql function against string injection not working
This API is really useful, and I'm using it with PHP. I would propose here a little improvement.
This is a sort of pepper. I want to premise I know that pepper techniques are already proposed in past ( https://wiki.php.net/rfc/password_hash#the_api_does_not_support_pepper ), but I think my propose is better, because:
I think it's a good idea to have the possibility to add x characters as a variable of your ini file, an "ini salt". When password_hash() is invoked, by default it will generate a salt with length (y - x), when y is the length of the salt the algorithm requires. password_hash() will complete it with the ini salt and generate the hash. The output will have only the salt generated by password_hash(), not the ini salt. Of course password_verify() must also complete the salt using the ini one.
This way you have not to store the ini salt to a PHP variable, you can read the ini each time. Of course if you'll change the ini salt you have to rehash all the db passwords. This should be well documented inside the ini. Furthermore this doesn't protect you if all the site, not only the db, is compromised.
Of course ini salt should be ignored if the salt is specified as password_hash option, and if the salt stored inside the hash is complete, password_ verify() (and password_needs_rehash() ) should not read the ini salt. Furthermore if the resulting salt length is incompatible with the algorithm, the functions should raise an error.
Let me know what do you think about.
You've stated that at least PHP 5.3.8 is needed because in PHP 5.3.7 has been a bug in crypt (#55439). But this bug has been introduced in PHP 5.3.7 and is fixed in PHP 5.3.8. The correct PHP version requirement would be to allow all PHP 5.3 versions except 5.3.7.
Setting this more correct version requirement makes it possible to use your PHP implementation of the PHP 5.5 password library with more PHP installations. Debian for example has PHP 5.3.3 and any installation with php of debian repo wouldn't be able to use the library.
hello
On line 73 php password file.
The variable $resultLength is no use remaining.
This is technically a bug, but also not something that should ever really happen (although I'm glad it did because it brought up a bug in my code).
Running password_hash with 'NULL' as the password returns a hash;
> php -r "var_dump(password_hash(null, PASSWORD_BCRYPT));"
string(60) "$2y$10$VL3.TMjNH9/Q7LYIQ24kw.AQIfLU7CDFMl2ZLoBAOnFauUUq50KBa"
Running the same with this returns 'NULL'.
Ideally this should mirror the behaviour of the standard password_hash and thus should return a hash instead.
What is the license that covers this work ?
I worked on a fork that adds further backward compatibility (by removing the 1:1 feature compat contraint with the C implementation):
https://github.com/nicolas-grekas/Patchwork/blob/master/core/compat/class/Patchwork/PHP/Shim/Php550.php
I put my usual license header, mentioned the original authors (you and some code from phpass), but I would be much more comfortable when the license of this library is clarified.
Many thanks
Hi,
I'm doing the password encoding command in Symfony. Here is the output of travis here : https://travis-ci.org/symfony/symfony/builds/53964672
I can see that the encoding of the password is not the same as PHP is doing.
The salt used is : AZERTYUIOPOfghjklytrert,yuiolnbcxdfghjkytrfghjk
. The coma is a problem as in your script you encode the the salt in base_64 if it does not match this regex => https://github.com/ircmaxell/password_compat/blob/master/lib/password.php#L98
I changed my test by removing this coma, but, I guess it's not okay that the lib doesn't work like PHP does.
Thank you in advance for your help
OK at a very inopportune moment, I learn that this third party library just doesn't work on one of my servers that badly needs these password hash functions.
my code:
require_once 'password.php'; // the password_compat library
var_dump(password_hash("foo", PASSWORD_DEFAULT));
the output:
bool(false)
BOOO! I'm pissed. The code seems to get all the way through the process until it gets to this point:
$hash = $hash_format . $salt;
echo "hash is $hash\n";
echo "password is $password\n";
$ret = crypt($password, $hash);
echo "ret is $ret \n";
if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
die("returning false, string length is " . PasswordCompat\binary\_strlen($ret) . " whereas resultLength is " . $resultL$
return false;
}
I've no idea what's failing but it looks like the crypt function isn't holding up its end of the bargain. The output:
hash is $2y$10$4yM5nzUadsYLVf.TBcowte
password is foo
ret is $2zJyhpjk3l9E
returning false, string length is 13 whereas resultLength is 60
What the heck?
I have tested the password_verify of PHP that does not verify correctly. I am using centOS and PHP version 5.3.3.
It is always to return true with different passwords when i verify it. Is my code has bug?
Here is my code:
$password = 'k32AlGOPqvCzoh*Sp(Hdrr26]M=lQb00R&W=hew|-|([(03vp==A8%m?l=eA2^bs_|\qVV3WZ';
$verify_pw = 'k32AlGOPqvCzoh*Sp(Hdrr26]M=lQb00R&W=hew|-|([(03vp==A8%m?l=eA2^bs_|\qVV3WZasdasdasdasdqweqa13123';
$options = array(
'cost' => 15
);
$hash = password_hash($password, PASSWORD_BCRYPT,$options);
var_dump(password_verify($verify_pw ,$hash)); // sometime true sometime false
I'm not sure if this is a great idea or not but it is an idea none the less.
Would it be beneficial on a performance standpoint to check if the native password_* functions where available and if so simple pass things off to them?
Don't we need to this library on php 5.5? Or is there any cases that we may still need this library on php 5.5?
@ircmaxell
If the custom salt contains a character which isn't a valid bcrypt Base64 digit, the library will silently encode the salt. This is a huge problem. If the salt is just a faulty Base64 string (e. g., it uses +
instead of .
), it will be double-encoded, causing it to effectively lose 33 bits.
The library also requires the custom salt to always contain 22 bytes, even if a raw salt would only have to be 16 bytes long. This forces us to either generate 6 useless extra bytes or pad the string with garbage data.
Unfortunately, there's no simple fix, because it's not always possible to distinguish between a raw salt and a pre-encoded one. If the string happens to contain only Base64 digits, it could be both.
I suggest to either ask for raw salts or for pre-encoded salts, not both. Using raw salts is probably the better solution:
Either way, it should be only one possible format, and the manual should clearly state what the function expects.
Can you make a new Release?
Hello,
I'd like to know when the next tagged release will be? Your last tag is from over a year ago and there are some fixes that I'd like in production.
Thanks!
I have tried generating and verifying hashes using the example code for this library provided in its README, and they fail to verify in node.js's bcrypt module, python's bcrypt module, and ruby's bcrypt module. These other three generate hashes compatible with one another, with the PHP bcrypt implementation being the odd one out. I'm not a cryptographer so I'm not sure where to go about looking for the problem. Wish I could provide more info; if some test cases would help, I'd be glad to provide them.
Are there plans to support the PASSWORD_ARGON2I
algo, or is this library aiming to specifically be a compat layer exactly to match PHP 7.0.x?
There currently doesn't seem to be a way to do something like this, which would be useful:
$cost = max($myCost, PASSWORD_BCRYPT_DEFAULT_COST);
The use case here is wanting to bump the default cost now, but not be limited in case a new PHP version bumps the default cost.
This seems to be an issue with the native password API too, so apologies if this isn't the best place for the issue.
It seems to fail on line 130
if (!is_string($ret) || strlen($ret) <= 13) {
return false;
}
I changed 13 to 12 and it works.
It was used like this
$string = 'test';
return password_hash($string, PASSWORD_DEFAULT);
// returns $2rcByx51ejoM
Running PHP 5.3.6
A common use-case:
function login($username,$password) {
$hash=findUser_getHash($username);
if ($hash) return password_verify($password,$hash);
// user does not exist => verify not called => login() returns faster:
return false;
}
As (e.g.) bcrypt is specifically designed to be slow, it's shouldn't be difficult to determine remotely whether a given username exists -- where as Best Practice says: "Don't disclose that username is correct, when password is not".
Therefore, to equalize the timing, password_verify(???), or something similar, must be called before returning false.
One workaround that comes to mind:
function login($username,$password) {
$hash=findUser_getHash($username);
$dummyHash=password_hash('dummy',...default...); // always called!
if ($hash) return password_verify($password,$hash);
password_verify('',$dummyHash); // timing correction
return false;
}
Is there a better way? And, if not, shouldn't there be a better way?
When I run the following code in PHP 5.4.45
$password = 'XXX';
$salt = 'XX';
var_dump(password_verify($password, crypt($password, $salt)));
I get false
as result. When I run the same code with PHP's native password_verify
function, I get true
altough it states that returns false on error, most of the error checks return null instead, so a " === false " comparison would fail. also, it is inconsistent with password_verify that returns false on "crypt missing" error
I use the latest version of PHP 5.4 and I am currently developing a system that uses the password_compat library.
Since I develop in a localhost environment, I have noticed inconsistency in the password_verify function whenever I use it in my function each time the server is restarted (i.e. by shutting down my servers and starting them again next time) and I try to use the stored password stored in my MySQL DB.
This library has been a life-saver for me but this issue has been bothering me for quite some time.
hello everyone
i have a question:
how can use password_compact in codeigniter version 3?
please help me
thank you
This should be pretty easy to do, since PHPUnit does support *.phpt format. All that is needed is to copy them from php-src and add
--INI--
auto_prepend_file=lib/password.php
to each test file.
Hi,
I'm just wondering whether this repo is still being actively maintained by its owner? I'd been recently considering trying to implement something like what this package is intended to do, and it seems like package could be quite useful, but I can see four open pull requests to this repo currently, the oldest open since 2013 and the newest since 2015, and a number of forks with commits made since after the last commit to the parent; Not sure whether it would be better to work directly from the parent, from one of the forks, or something else.
Your input appreciated, and cheers. :-)
I don't know why this check is there, but the namespace and so the function name (full qualified name) are unique and thus this it is not necessary in my opinion (and normally a namespace is just defined once), or am I wrong?
password_compat/lib/password.php
Line 256 in 1ecb013
password_compat/lib/password.php
Line 316 in 1ecb013
default value of $algo should be PASSWORD_DEFAULT in password_hash($password,$algo,$options)
in
https://github.com/ircmaxell/password_compat/blob/6f127097b6260194a14e22c37bcb64b4a0eb919a/lib/password.php
In an internal testsuite I discovered the following regression regarding known salts in the v1.0.4 release:
public function testKnownSaltRegression() {
$hash = password_hash("foobar", PASSWORD_BCRYPT, array("cost" => 04, "salt" => "OyuaiuF.Bcw7mNCak/3Au7c="));
$this->assertEquals('$2y$04$T3l1YWl1Ri5CY3c3bU5DYOiIKqbbPpdcIiqpnkLoRwJkELw/uCaUO', $hash);
}
PHP | v1.0.3 | v1.0.4 |
---|---|---|
5.5.14 | ✔ | ✔ |
5.4.39 | ✔ | ✗ |
It's not really an issue for me because in the real application, a known salt is never used.
I suspect the provided salt OyuaiuF.Bcw7mNCak/3Au7c=
is simply malformed/not valid and the issue is related to #38.
Because this is not a real issue for me feel free to close this issue.
Nevertheless I would love to understand what's the root cause of this regression and what was wrong with the salt in the first place.
I also fully understand that providing custom salts is a bad idea and will most probably remove that from my internal interfaces completely.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.