Understanding password_hash and BCRYPT [duplicate] - php

This question already has answers here:
Using PHP 5.5's password_hash and password_verify function
(4 answers)
How to use PHP's password_hash to hash and verify passwords
(5 answers)
How can bcrypt have built-in salts?
(5 answers)
Closed 5 years ago.
Cryptography
Recently I have been doing a lot of research into cryptography. It led me to discovering that, not only do we have salt, we also have pepper (yes I really only just found out about pepper).
Hashing passwords
This all started when I inherited a project in which passwords were not hashed. Even as a n00b I know that this is just silly.
I have a basic understanding of hashing, but what I knew was apparently outdated.
Original solution
My original hashes and salts were achieved by:
Getting the user's password
Combining it with a salt
Hashing it using MD5
Storing both, the salt and the hash in a table
This worked well, but I am leaning towards using a better technique
New technique
The new way I wish to try is by using PHP's password_hash and password_verify.
Having created a quick test, I can see that I can return a true value with password_verify on a given hash. The one thing that is puzzling me is where the salt comes in?
On the documentation I can see that there was an option to specify a salt in the options array but that is deprecated as of PHP 7.0.
My attempt
I have created some code (untested) just to demonstrate. See below code.
<?php
$pepper = "myPepper";
function register($username, $password)
{
// add the pepper
$password .= $pepper;
// hash the password
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 10]);
// insert into table
$query = $mysqli->prepare("INSERT INTO users(username, password) VALUES(?, ?)");
$query->bind_param("ss", $username, $hash);
// check the success
if ($query->execute())
return true;
else
return false;
}
function login($username, $password)
{
// create the query
$query = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$query->bind_param("s", $username);
// check if successful
if ($query->execute())
{
// get password from the database
$query->store_result();
$query->bind_result($hash);
$query->fetch();
// verify the passwords are the same
if (password_verify($password . $pepper, $hash))
return true;
else
return false;
}
}
?>
register function
Both these functions are very basic as I just wanted to show a point.
This function takes some parameters (in this case username and password and adds them to the database.
Now, I know how to add my pepper to the password as that is a simple concatenation, but the salt is randomly generated but never returned meaning I have no idea what it is.
login function
Again, very basic in what it does.
Since I have not used password_verify before I am not entirely sure I know the best approach to use to attain the user's password.
With my old login scripts they looked something like this:
function login($username, $password)
{
// create the query
$query = $mysqli->prepare("SELECT salt FROM users WHERE username = ? AND password = ?");
$pass = md5($password);
$query->bind_param("ss", $username, $password);
// check if successful
if ($query->execute())
{
$query->store_result(); // store result to gain access to num_rows
// verify if password and usernames match
if ($query->num_rows == 1)
return true;
else
return false;
}
}
Where I would just hash the password and pass it as a parameter into the SQL query.
With bcrypt I have to withdraw the password from table and then use it in another query. (At least that is what I believe I have to do currently).
Finally, the question part
Disclaimer
I apologise if I offended anyone with my lack of knowledge and / or my poor explanation of my knowledge.
Your help
I crave knowledge. So what I don't know, I want to know. (Obviously to an extent, I like to learn about computers and systems and programming etc).
Password hashing is essential these days and getting it right is very important and that is why I am writing this essay question.
Question(s)
Is my understanding of bcrypt correct?
Does it really not store a salt?
In my basic example of login and register, have I implemented password_hash and password_verify?
Update 1
Following all the comments, I would just like to post this update.
As stated above I like to learn, so when I came across this function it started confusing me because I didn't know what was happening.
I am going to post some examples to try and effectively get my confusion across to everyone.
Let's take this script for example:
<?php
// everything below is hard coded for simplicity (would actually be extracted from a database)
$password = "myPassword"; // the password from the database
$salt = "mySalt"; // I have hard coded this for simplicity
$hash = md5($salt . $password);
// check login status
if (md5($salt . $_POST["password"]) == $hash)
return true;
else
return false;
?>
In this example I understand how the hashing works.
I am storing the password with a salt and hashing it. Then I am checking the posted password with the salt and hashing that. If the two match then I succeed with the login, otherwise the login has failed.
Now, let's take the following example.
<?php
// everything below is hard coded for simplicity (would actually be extracted from a database)
$password = "myPassword"; // the password from the database
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 10]); // salt is taken care of
// check login status
// again I am keeping this so simple (it might not work per se but I just want to learn about the functions)
if (password_verify($_POST["password"], $hash))
return true;
else
return false;
?>
Confusion
I start getting confused when I am running password_verify.
I understand that the salt is taken care of by default in password_hash, but in the first example I know the salt and so I can perform a hash with the posted password to check if they match.
Why, then, does password_verify succeed in verifying the posted password without me having given it the salt?
Surely a salt is designed to make each password unique but somehow password_verify will succeed. I have used var_dump to dump the hash made from password_hash and it changes upon refresh. This really is where the confusion comes from. If the hash of "test" can change to a different hash each refresh, then how does password_verify know the posted password is correct?
I know that I can write a function to verify the user's password. What I really want to know is how can how PHP's password_verify manage to validate to true every time, despite the fact that my hash will change with each refresh.
Note: I understand that I will be storing the password in a database so the refreshing won't be a problem, but I was doing so to try and understand the function.
Comments
#RiggsFolly I know that the salt is done for me. The question is "How does the verify function know the random salt created in the hash function in order to validate to true?"
#RiggsFolly I understand the salt option was deprecated. Had it not been (and I was able to pass it a salt), I think I would understand this function a lot more. The whole idea of the verify function successfully validating a password against a hash without knowing the salt is actually blowing my mind.
Perhaps I am just being stupid.
#Alex Howansky How is the salt returned? A string such as $hash = $2y$10$Vaj4ZonpRJjE6kmfQffvOOeIVW3ZV31JJYVY79GtZ3GtioZKtDwku means nothing, yet somehow password_verify("test", $hash") returns true.
#Machavity Having read the link, I can see that the salt is at the start of the hash, but how can the salt be in the final hash? I apologise for my confusion, and apparent stupidness, but I simply wish to understand password hashing so that I am better prepared for future uses.
#Fred -ii- Those custom function were just simple for the example (I didn't want to link an entire page of code). That being said, my current usage is in a self defined Class called User, where I have a private variable called $conn which stores the mysqli connection. Then from that I use $this->conn->prepare("SELECT * FROM ...") to access the database.
Is that bad for scoping? What is the preferred way to store the connection within a self defined Class?

Related

I can't compare password from my database and the one inputted

I am using php crypt function to make a password secure, but when I try and compare a password entered to a one in the database it will not work.
here is my code to create the password in the first place:
$crypt_password = crypt($_POST['confirm-password']);
here is me trying to compare to the password in another function:
$input_crypt_password = crypt($_POST['input-pw']);
if ($input_crypt_password == $dbpassword){
// do change password function
}
This is not working.
when i print both passwords the are different.
why are the passwords different even though I am entering the same password and using crypt function on both?
can anyone point me in the right direction?
From the docs
Example #1 crypt() examples
<?php
$hashed_password = crypt('mypassword'); // let the salt be automatically generated
/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo "Password verified!";
}
?>
The code in the question will effectively generate a new hash every time it's called - the existing password hash needs to be passed as the salt to get a consistent result.
As also mentioned in the docs:
Use of password_hash() is encouraged.
I'd go further and say you definitely should be using password_hash instead of calling crypt for password usage (assuming php >= 5.5); in any case though for whichever whatever tools/methods you're using - please read the docs to know how to use them.
Don't use crypt directly for passwords.
If you have PHP 5.5+, than use the built in password_hash function, otherwise if you have PHP 5.3.7+ use the polyfill for this function.
Try to something like this.
$crypt_password = crypt($_POST['confirm-password'],salt);
$input_crypt_password = crypt($_POST['input-pw'],salt);
if ($input_crypt_password == $dbpassword){
// do change password function
echo "Password match successfully!";
}
Here salt parameter to base the hashing on. If not provided, the behaviour is defined by the algorithm implementation and can lead to unexpected results.
I don't know what to say that will add more detail than what everyone else has already said...
So, in modern day hash/unhashing algorithms it would be unsafe to store passwords using standard hashing functions (e.g. MD5 / SHA256) as it is quick and easy to unhash this type of entry.
password_hash() as referenced in other answers and comments should be you're #1 way to safely store passwords as it uses a one way hashing algorithm.
You should read this page!
And then in response to your original question, use hash_equals() function to compare passwords.
As many guys here said, you should use password_hash php function.
Here you can see a simple example how to use it:
<?php
$password = '123456';
$userInput = '123456';
$storedHash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($userInput, $storedHash)) {
echo 'OK';
} else {
echo 'ERROR';
}
Also as mentioned before, if you use older version of PHP, you can install polyfill.
Did you trim the input before saving in db and while making the comparison. Since the input is coming from browser this may be a reason why it is not matching. otherwise this https://stackoverflow.com/a/41141338/1748066 seems appropriate.

Password does not match after being encrypted using crypt() and password_hash() function

I modified my old post. I tried the crypt() function and now trying to work with password_hash() and password_verify() to verify the encrypted password coming from database but on each call, password_hash() function retuns a different encrypted string and password_verify() cannot match it.
This is how I am doing this.
//please ignore the syntax error if any
$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass; // displays the random strings on each page refresh.
Once password is saved into database does not get match during the login process. Below is my actual function.
private function process_data($password){
$password = __STR.$password.__STR;
return password_hash($password, PASSWORD_BCRYPT);
}
private function processed($login_password, $dbpassword){
$login_password = __STR.$login_password.__STR;
return password_verify($login_password, $dbpassword);
}
On each function call for creating a hashed string for password, the function returns the different string next time.
Ok, Let's go through this one by one.
First, it's hashing, not encryption. Encryption is two-way, hashing is one way. We want to hash. We never want to encrypt. Yes, terminology matters. Please use the correct terminology.
Next, each call to password_hash is supposed to return a different hash. That's because it's generating a strong random salt. This is how it was designed, and how you really should be using it.
Further, DO NOT do the "pepper" thing of adding __STR before and after the password. You're doing nothing but potentially weakening the users password (which is not good). If you want more information around why that's a bad idea: Read This Answer.
Continuing, I would highly recommend that you do not use crypt directly. It is actually surprisingly easy to screw up and generate extremely weak hashes. This is why the password_* api was designed. crypt is a low level library, you want to use a high level library in your code. For more information on ways to screw up bcrypt, check out my blog: Seven Ways To Screw Up Bcrypt.
The Password API was designed to be a simple, one-stop shop. If it's not working for you check the following things:
Are you using PHP >= 5.5.0? Or are you using PHP >= 5.3.7 with password_compat?
Is your database column wide enough?
It needs to be at least 60 characters long.
Are you checking that the result of the function is a string, and not bool(false)?
If there is an internal error, it will return a non-string from password_hash.
Are you getting any errors?
Have you turned on error_reporting to its maximum setting (I recommend -1 to catch everything) and checked that the code isn't throwing any errors?
Are you sure you are using it correctly?
function saveUser($username, $password) {
$hash = password_hash($password, PASSWORD_BCRYPT);
// save $username and $hash to db
}
function login($username, $password) {
// fetch $hash from db
return password_verify($password, $hash);
}
Note that each one should be called only once.
Are you using PHP < 5.3.7 with password_compat? If so, this is your problem. You are using the compatability library on an unsupported version of PHP. You may get it to work (certain RedHat distributions have backported the necessary fixes), but you are using an unsupported version. Please upgrade to a reasonable release.
If all else fails, please try running this code and reporting back the output:
$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;
echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "\n";
If that returns Fail, you are running an unsupported version of PHP and should upgrade. If it returns pass, than the error is somewhere in your logic (the library is functioning fine).
The best way to store passwords is to use PHP's function password_hash(). It automatically generates a cryptographically safe salt for each password and includes it in the resulting 60-character string. You won't have to worry about the salt at all!
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
Your own scheme is very weak, first you are using MD5 which is ways too fast for generating password hashes, then you use a static salt, which defeats the purpose of a salt. Maybe you want to have a look at my tutorial about safely storing passwords.
Edit to answer updated question:
It is not necessary to add the __STR to the password (if you want to add a pepper there are better ways), but your example functions should actually work. The returned value of password_hash() will be different each time because of the random salt. This is correct, the function password_verify() is able to extract this salt for the verification. In your case the database field is probably the problem. Make sure it can hold a 60 character string.

How to decrypt password in mysql

Going straightly to my question, I have read about encrypting password while registering the user.Till here im able to register users with encrypted password by using PASSWORD('$PASS'); where $PASS is the users password. My sql table details is as follows :-
FNAME
LNAME
EMAIL
PASS // USED ENCRYPTION AS PASSWORD('$PASS'); HERE.
I can't understand how to decrypt the password & use futher in my code i use the following code to use decrypt the password but its not working. !
<?php
$EMAIL = $_POST['email'];
$PASS = $_POST['pass'];
mysql_connect('host', 'user', 'pass');
mysql_select_db('userdb');
$results = mysql_query(sprintf("SELECT FNAME,LNAME,EMAIL,PASS FROM `details`
WHERE PASS=PASSWORD('$PASS')",
mysql_real_escape_string($EMAIL))) or die(mysql_error());
while($row = mysql_fetch_assoc($results))
{$rows[1] = $row;}
if(!($_COOKIE['pass'] == $rows[1][PASS]))
//cookie is set while registering user , which is the decrypted(original) value of password.
{ die("Error occured"); }
else { echo "Password entered is correct"; }
////.....my further code here.
?>
Its showing Error occured on the page, which means the password is incorrect. I Also add that this code was working correctly before encryption of password in database.Im new to encryption process ,Your little help is needed which will help me to learn more. Thanks in advance.
You don't encrypt passwords, you hash them.
The point is, that you don't actually need the users password, you just need to know that they know it.
As an example, an absolutely terrible way to do that might be a simple count: e.g.
if the users password was 'horse123', you might store that as 8. Then you just count the letters in the password, and if it's 8, you know it's right.
That means that you never need to know the actual password.
Clearly that's awful, as there are many passwords with 8 characters! We need something with less 'collisions'.
Instead, we use one way hash functions. The most common way to do this is to use an MD5 hash. (it's not the best, but it's simple to explain). For how to actually do this, look at http://www.openwall.com/phpass/.
For the short and sweet version:
Get the users password, and do something like:
$pass = md5('somerandomtextthatyouknow'.$_POST['password']);
then, store that in your DB.
When they log in, you do the same again, and check that the hash in your DB.
This way, you never need to know the actual passwords, the passwords can be as long as you like, and if your database is stolen, the hashes are not useful to anyone (because we added in that random text).
So, now you understand that, read:
http://www.openwall.com/phpass/
and absolutely read up on SQL injection and SQL prepared statements, else this is all a bit pointless!
You shouldn't encrypt passwords. You should hash them. This way they can't be decrypted.
You can read more about it here.
Best solution is to use HASH code instead of using encryption and decryption.
md5($pass) - give you 32 bits unique hash code
similarly sha256(), hash()...etc
store these hash codes in your database at the place of password.
Hash code are one way. So it is more secure for your users.
Click here for a more comprehensive way of protecting your passwords and login access using PHP and MYSQL

Encrypt password before storing in database?

I have a password being passed from my iPhone app to the database via a php script, user.php.
The variable $pass is populated by the following:
$pass = str_replace("'", "", $_REQUEST['pass']);
How can I encrypt this before it's inserted into my database? I've read a little about the different techniques, but looking for the best way to manage this.
Thanks to everyone.
While the answer below is technically still correct, php has new recommendations with regards to the hashing algorithms to use. Their recommendation, as of php >= 5.5.0, is to use the password_hash and password_verify functions to hash and verify hashed passwords . As an added benefit, these functions automatically include an individualized salt as part of the returned hash, so you don't need to worry about that explicitly.
If you don't care about retrieving the actual password's value (from the database encrypted value), you can run a one-way hash algorithm on it (such as sha1). This function will return a specific length string (hash) which cannot be used to find the original string (theoretically). It is possible that two different strings could create the same hash (called a collision) but this shouldn't be a problem with passwords.
Example:
$pass = sha1($_REQUEST['pass']);
One thing, to make it a little more secure is to add a salt to the hash and run the hash function again. This makes it more difficult to generate a password hash maliciously since the salt value is handled server-side only.
Example:
$pass = sha1(sha1($_REQUEST['pass']).sha1("mySalt#$#(%"));
Use php's crypt library. Md5 is not encryption, it is hashing.
Also, salt your passwords. Why?
This answer
Another good answer
First, you should create a random user salt. Then you should store that and the password hash in the database.
$salt = md5(unique_id().mt_rand().microtime());
$pass = sha1($salt.$_REQUEST['pass']);
and save the $salt and $pass in the database. Then when they go to login you look up their row and check the hash:
$user = query('SELECT * FROM `user` WHERE username = ?', array($_REQUEST['username']));
if($user)
{
// If the password they give maches
if($user->pass === sha1($user->salt. $_REQUEST['pass']))
{
// login
}
else
{
// bad password
}
}
else
{
// user not found
}
Creating a user salt for each account insures rainbow tables are useless and anyone that broken into your server would have to brute-force each password.
Use crypt with some salt. Such as
$user = strip_tags(substr($_REQUEST['user'],0,32));
$plain_pw = strip_tags(substr($_REQUEST['pass'],0,32));
$password = crypt(md5($plain_pw),md5($user));
as on http://www.ibm.com/developerworks/opensource/library/os-php-encrypt/
Most basic: Hash it with MD5 or SHA1
$newpass = md5($_REQUEST['pass']);
or
$newpass = sha1($_REQUEST['pass']);
Recently I started storing the username hashed as well, so login attempts are secure using only hashed data for comparisons.
You can "salt" the hashes with extra data so if they are compromised, it's value cannot be found (try googling some simple hashed words).. i.e. use a site-wide string just to alter the standard hash like md5("mySiteSalt!!" . $_REQUEST['pass']); or something more advanced.
You should use SHA1 to hash your passwords for storage in the database. It's the simplest, yet most effective way to store passwords:
$password = sha1($password);
It's also exceptionally safe. Though the integrity of it is beginning to creep, it's rather easy to upgrade this function to SHA-256 (which is incredibly secure).
To find out why md5, sha1 and their speedy friends might not be a good idea, you should read the post Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes by Thomas Ptacek. The gist:
Finally, we learned that if we want to
store passwords securely we have three
reasonable options: PHK’s MD5 scheme,
Provos-Maziere’s Bcrypt scheme, and
SRP. We learned that the correct
choice is Bcrypt.
Note: it's PHK, not php.

How can I store my users' passwords safely?

How much more safe is this than plain MD5? I've just started looking into password security. I'm pretty new to PHP.
$salt = 'csdnfgksdgojnmfnb';
$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
WHERE username = '".mysql_real_escape_string($_POST['username'])."'
AND password = '$password'");
if (mysql_num_rows($result) < 1) {
/* Access denied */
echo "The username or password you entered is incorrect.";
}
else {
$_SESSION['id'] = mysql_result($result, 0, 'id');
#header("Location: ./");
echo "Hello $_SESSION[id]!";
}
The easiest way to get your password storage scheme secure is by using a standard library.
Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.
The new PHP password API (5.5.0+)
If you are using PHP version 5.5.0 or newer, you can use the new simplified password hashing API
Example of code using PHP's password API:
<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);
// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
(In case you are still using legacy 5.3.7 or newer you can install ircmaxell/password_compat to have access to the build-in functions)
Improving upon salted hashes: add pepper
If you want extra security, the security folks now (2017) recommend adding a 'pepper' to the (automatically) salted password hashes.
There is a simple, drop in class that securely implements this pattern, I recommend:
Netsilik/PepperedPasswords
(github).
It comes with a MIT License, so you can use it however you want, even in proprietary projects.
Example of code using Netsilik/PepperedPasswords:
<?php
use Netsilik/Lib/PepperedPasswords;
// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');
$hasher = new PepperedPasswords($config['pepper']);
// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);
// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
The OLD standard library
Please note: you should not be needing this anymore! This is only here for historical purposes.
Take a look at: Portable PHP password hashing framework: phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.
Example of code using phpass (v0.2):
<?php
require('PasswordHash.php');
$pwdHasher = new PasswordHash(8, FALSE);
// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );
// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
PHPass has been implemented in some quite well known projects:
phpBB3
WordPress 2.5+ as well as bbPress
the Drupal 7 release, (module available for Drupal 5 & 6)
others
The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.
For more information on password storage schemes, read Jeff`s blog post: You're Probably Storing Passwords Incorrectly
Whatever you do if you go for the 'I'll do it myself, thank you' approach, do not use MD5 or SHA1 anymore. They are nice hashing algorithm, but considered broken for security purposes.
Currently, using crypt, with CRYPT_BLOWFISH is the best practice.
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down.
Your users will be much safer if you used parameterized queries instead of concatenating SQL statements. And the salt should be unique for each user and should be stored along with the password hash.
A better way would be for each user to have a unique salt.
The benefit of having a salt is that it makes it harder for an attacker to pre-generate the MD5 signature of every dictionary word. But if an attacker learns that you have a fixed salt, they could then pre-generate the MD5 signature of every dictionary word prefixed by your fixed salt.
A better way is each time a user changes their password, your system generate a random salt and store that salt along with the user record. It makes it a bit more expensive to check the password (since you need to look up the salt before you can generate the MD5 signature) but it makes it much more difficult for an attacker to pre-generate MD5's.
With PHP 5.5 (what I describe is available to even earlier versions, see below) around the corner I'd like to suggest to use its new, built-in solution: password_hash() and password_verify(). It provides several options in order to achieve the level of password security you need (for example by specifying a "cost" parameter through the $options array)
<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));
$options = array(
'cost' => 7, // this is the number of rounds for bcrypt
// 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>
will return
string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."
As you might see, the string contains the salt as well as the cost that was specified in the options. It also contains the algorithm used.
Therefore, when checking the password (for example when the user logs in), when using the complimentary password_verify() function it will extract the necessary crypto parameters from the password hash itself.
When not specifying a salt, the generated password hash will be different upon every call of password_hash() because the salt is generated randomly. Therefore comparing a previous hash with a newly generated one will fail, even for a correct password.
Verifying works like this:
var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
I hope that providing these built-in functions will soon provide better password security in case of data theft, as it reduces the amount of thought the programmer has to put into a proper implementation.
There is a small library (one PHP file) that will give you PHP 5.5's password_hash in PHP 5.3.7+: https://github.com/ircmaxell/password_compat
That's fine with me. Mr Atwood wrote about the strength of MD5 against rainbow tables, and basically with a long salt like that you're sitting pretty (though some random punctuation/numbers, it could improve it).
You could also look at SHA-1, which seems to be getting more popular these days.
I want to add:
Don't limit users passwords by length
For compatibility with old systems often set a limit for the maximum length of the password. This is a bad security policy: if you set restriction, set it only for the minimum length of passwords.
Don't send user passwords via email
For recovering a forgotten password you should send the address by which user can change the password.
Update the hashes of users passwords
The password hash may be out of date (parameters of the algorithm may be updated). By using the function password_needs_rehash() you can check it out.
Here's a PHP + CouchDB.apache.org login system that doesn't store plaintext passwords.
According to the advice that I've read, it should be totally secure.
CMS login code : https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php#L56
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L171
app(s) specific business code :
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/ajax_login.php#L87
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L230
which in turn calls :
https://github.com/nicerapp/nicerapp/blob/2d479b3e22dce9e7073525481b775f1bf7389634/nicerapp/apps/nicer.app/webmail/recrypt.php#L2
and to edit the webmail app config data into the database :
https://github.com/nicerapp/nicerapp/blob/main/nicerapp/apps/nicer.app/webmail/ajax_editConfig.php

Categories