I'm creating a more secure user password table with the username, a strongly random salt, and a salted/hashed password in php like so:
$salt = bin2hex(openssl_random_pseudo_bytes(64));
$pass = hash_hmac('sha512', $plainTextPass, $salt);
$this->user->password = $pass;
$this->user->salt = $salt;
$this->user->save();
I'm trying to reproduce this functionality in MySQL without much luck. I need to create some users when the DB server is deployed from a script. As I understand it the hash_hmac method just takes the salt, appends the password to it, then hashes it with sha 512. Seems like this would be easy enough to do in MySQL, but this:
INSERT INTO `users` (`email`, `username`, `password`, `salt`)
VALUES('test#example.com', 'admin', SHA2('9d1ebf3559baf9ffca61df5cec0b9eb067ae7912d097ad45f3a3086c39bf11292d92a7dfad497557fac7fbf7c24209db8b7696664d54c7e6bc55f720121bd38dadmin', 512), '9d1ebf3559baf9ffca61df5cec0b9eb067ae7912d097ad45f3a3086c39bf11292d92a7dfad497557fac7fbf7c24209db8b7696664d54c7e6bc55f720121bd38d');
In this case the salt is just a hard coded one I generated manually, and you can see I just append the password "admin" on to the end of it for the password field. This user creates, but I can't login with that admin username and password.
Should I be doing this a different way in MySQL?
An HMAC is not computed by concatenation, but as follows (see the link for more details).
HMAC (K,m) = H ((K ⊕ opad) ∥ H ((K ⊕ ipad) ∥ m))
However, as the name implies, it is a message authentication code, not a password hashing scheme. An algorithm that is designed for password hashing would be a better choice. scrypt, bcrypt, or PBKDF2 would be a good bet.
Related
I have an old web application with a few users registered that is using the unsecure hash("sha256", trim($_POST["password"])) to store the hashed password in MySQL database. Now I want to update the web application to use the more secure BCRYPT password_hash() however I don't want to email all registered users alerting them to change their password. So I was thinking on implementing BCRYPT on the sha256() hashed password this way:
To save the password I will sha256() hash the user's password:
$hashed_password = password_hash(hash("sha256", trim($_POST["password"])), PASSWORD_BCRYPT);
Then I will save the BCRYPT hashed password in the database.
And to verify the user's password I would simply do this:
$hashed_password = "select hashed_password from users where email = 'abc#email.com'";
if(password_verify(hash("sha256", trim($_POST["password"])), $hashed_password))
{
echo "Welcome";
}
else
{
echo "Wrong Password!";
}
This way I will just update the user's password in the MYSQL database by looping each registered user, then I will retrieve the sha256() hashed password, and finally I will just re-save it after it has been BCRYPTed with password_hash():
$new_password = password_hash($old_sha256_hashed_password, PASSWORD_BCRYPT);
$mysql->save_user_password($new_password, $user_id);
So users will still be able to login with their old password.
What do you think about this solution?
Is it still safe even if I sha256() hash the password before BCRYPT it?
Since your current hashing system (unsalted SHA256) is indeed very unsecure, you could give immediate protection to the passwords with double hashing. As soon as possible, when the user logs in the next time, I would switch to the new algorithm and remove double hashing.
Make old hashes more secure:
$doubleHashToStoreInDb = password_hash($oldUnsaltedSha256HashFromDb, PASSWORD_DEFAULT);
Doing this for each row will protect the otherwise unsecurely stored passwords. Note the PASSWORD_DEFAULT parameter, it should be prefered over a specific algorithm, because it is future proof. And mark the double hashes, so you can distinguish between double hashes and already converted hashes, see why.
Handle new user registrations:
$hashToStoreInDb = password_hash($_POST['password'], PASSWORD_DEFAULT);
Just use the new algorithm without double hashing.
Verify logins:
if (checkIfDoubleHash($storedHash))
{
$correctPassword = password_verify(oldPasswordHash($_POST["password"]), $storedHash);
if ($correctPassword)
storeConvertedHash(password_hash($_POST['password'], PASSWORD_DEFAULT));
}
else
{
$correctPassword = password_verify($_POST['password'], $storedHash);
}
// Hashes the user password with a deprecated hashing scheme
function oldPasswordHash($password)
{
return hash("sha256", trim($password));
}
Double hashes will be converted to the new password hash function, this is possible because we have the original user password at this moment. New hashes are verified with password_verify() which is a future proof and backwards compatible function.
Adapting the password algorithm to future hardware is not a one-time task, it will be necessary as soon as new hardware will become faster. PHP offers the function password_needs_rehash() to find out whether a rehashing is necessary, then you can also calculate a new hash and store it.
Sorry for being new in php programming, in my old project I use MD5 to encrypt the password, however, it is not secure enough and I found some resource on the internet suggest using password salt instead.
The problem is , I am using codeigniter, is there any helper/ library for this purpose / how to change my old code to support the generation of the password salt?
Thanks for helping. I am using PHP 5.2
And here is the old code to validate, while the user account generate by storing the md5($password);
function validate_credentials() {
$this->load->model('Secure_model');
$username = $this->input->post('username');
$password = md5($this->input->post('password'));
$is_valid = $this->Secure_model->validate('customer', $username, $password);
if ($is_valid) {
$data = array(
'user_id' => $this->get_user_id($username),
'user_name' => $username,
'is_logged_in_user' => true
);
$this->session->set_userdata($data);
redirect('profile');
} else {
$data['message_error'] = TRUE;
$data['main_content'] = 'front/login';
$this->load->view('front/includes/template', $data);
}
}
If you are really stuck with PHP 5.2 your best bet will propably be the phpass library, because there is no PHP support of the BCrypt algorithm.
PHP versions 5.3 and later will have native support of BCrypt, so you can use the PHP function password_hash() to hash a password. There is a compatibility pack for versions before 5.5.
// 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);
In every case you are doing right with discarding MD5 and switching to another algorithm. Make sure that you use an algorithm with a cost factor like BCrypt or PBKDF2, fast algorithms like SHA* are not appropriate to hash passwords. Salting is mandatory, though the salt can be stored in the database, it fulfills its purpose even if it is known.
look this part of my code I use to register an user:
public function addUser($data){
$sql = "INSERT INTO `user` salt=" . $this->db->escape($salt = substr(md5(uniqid(rand(), true)), 0, 9)) .", password=".$this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))).".......";
$this_>db->query($sql);
The information of salt and password are stored in your user table.
To retrieve the information and validate the password you do this:
$query = $this->CI->db->query("SELECT * FROM `user` WHERE email =".$this->CI->db->escape($email)." AND password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, SHA1(" . $this->CI->db->escape($password) . ")))))");
Here are some simple solutions.
You can use sha* hash functions , be careful in using md5 since it has a
higher rate of collisions than sha,
and also about your problem with salt, it is ok if you dont salt your
password, just make sure your users use a very good password with a combination of
lower and upper cases and with numbers and make them lengthy.
I would like to advise you to use bcrypt but since you are using 5.2 it has a bug on that version and certain password libs like PHPPASS and PHPLIB Cater Only to 5.3 and above. Best option is to upgrade to 5.3 so that you can use the php libs, but take care full caution the scripts.
As far as I know codeigniter does not have a built-in function for this...
To make a hash with PHP you need
the password
a true random salt
a slow hashing algorithm
By PHP your can create a true random salt by using mcrypt_create_iv().
To make the hash, you can use the crypt() or password_hash, which supports slow algorithms, like CRYPT_BLOWFISH. Forget md5, or sha1, they are too fast, so with the proper tool it is possible to find out passwords hashed by them.
$salt = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM);
$hash = password_hash($password, PASSWORD_BCRYPT, array('cost' => 11, 'salt' => $salt));
The password_hash() function can generate a true random salt automatically, so you don't have to generate it manually if you don't want. The salt will be appended to the hash.
Sadly PHP 5.2 does not have CRYPT_BLOWFISH support. So you have to use the PHPASS lib.
You should set a $config['salt] = '$%#~De#';// in your config file
//Inside your model or controller where you are getting your post values
$password = sha1($this->config->item('salt').$this->input->post->('password')));
This should give you has password
I'm using PHP's crypt function for password hashing/encryption, but I don't think I am doing it right because "nathan12" and "nathan123" both allow me to login to my account on my system (the actual password is "nathan123", and therefore "nathan12" or anything else should NOT allow me to login).
Here's what my system does when a user registers:
[...]
$salt = uniqid(mt_rand(), true);
$password = crypt($password, $salt); // '$password' is the inputted password
$insertUserStmt = $mysqli->prepare("INSERT INTO users (name,
username,
password,
password_salt,
email,
created) VALUES (?, ?, ?, ?, ?, ?)");
$insertUserStmt->bind_param("sssssi", $name, $username, $password, $salt, $email, time());
$insertUserStmt->execute();
[...]
It inserts the hashed/encrypted password ($password) into the database along with the $salt.
When someone tries to login, the following is done to check if the user has inputted the correct password for the username they inputted:
[...]
// $password_salt is from DB; $password is inputted password
$password_crypt = crypt($password, $password_salt);
// $login_password is from DB
if($password_crypt == $login_password) {
[...]
I'm probably not even using the crypt function properly, but according to the PHP docs the first parameter is a string (the password) and second is the salt.
The standard DES-based crypt() [...] only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
source
Use a salt that starts with $<algo>$ to use something other than DES. See the crypt() documentation for details.
You should be using password_hash() instead of crypt, for the reasons you mention: "I'm probably not even using the crypt function properly". You say you are getting the salt from the DB... this sounds insecure. with password_hash() you can let PHP handle the salting for you in a secure manner.
More details on why this is superior:
http://www.sitepoint.com/hashing-passwords-php-5-5-password-hashing-api/
You should use more than just a password salt to encrypt passwords.
You can store a random string in your configuration file.
$config['passwordKey'] = 'asjdfa783#H$Khjsdfhas78a734J%JSDGK2348235hxmfdA';
And append it to $salt when encrypting. This way if the database is compromised, and your file system is not, then attackers can't decrypt your database password hashes. This should be essential to protect the users information on other sites with identical login information.
To hash your passwords, password_hash is a simple crypt() wrapper specially configured for password hashing!
(source)
$password = password_hash($password, PASSWORD_BCRYPT, array(
'cost' => 60,
'salt' => $salt . $config['passwordKey']
));
Probably a very newbie question but, Ive been reading around and have found some difficulty in understanding the creation and storage of passwords. From what i've read md5/hash passwords are the best ways to store them in a database. However, how would I go about creating those passwords in the first place?
So say I have a login page with user bob, and password bob123
- how will I
1. get bobs password into the database to begin with (hashed)
2. how do I retrive and confirm the hashed password?
Thanks
Edit 2017/11/09: Be sure to take a look at the answer from O Jones.
First off MD5 isn't the greatest hashing method you could use for this try sha256 or sha512
That said lets use hash('sha256') instead of md5() to represent the hashing part of the process.
When you first create a username and password you will hash the raw password with some salt (some random extra characters added to each password to make them longer/stronger).
Might look something like this coming in from the create user form:
$escapedName = mysql_real_escape_string($_POST['name']); # use whatever escaping function your db requires this is very important.
$escapedPW = mysql_real_escape_string($_POST['password']);
# generate a random salt to use for this account
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$saltedPW = $escapedPW . $salt;
$hashedPW = hash('sha256', $saltedPW);
$query = "insert into user (name, password, salt) values ('$escapedName', '$hashedPW', '$salt'); ";
Then on login it'll look something like this:
$escapedName = mysql_real_escape_string($_POST['name']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$saltQuery = "select salt from user where name = '$escapedName';";
$result = mysql_query($saltQuery);
# you'll want some error handling in production code :)
# see http://php.net/manual/en/function.mysql-query.php Example #2 for the general error handling template
$row = mysql_fetch_assoc($result);
$salt = $row['salt'];
$saltedPW = $escapedPW . $salt;
$hashedPW = hash('sha256', $saltedPW);
$query = "select * from user where name = '$escapedName' and password = '$hashedPW'; ";
# if nonzero query return then successful login
you have to reason in terms of hased password:
store the password as md5('bob123'); when bob is register to your app
$query = "INSERT INTO users (username,password) VALUES('bob','".md5('bob123')."');
then, when bob is logging-in:
$query = "SELECT * FROM users WHERE username = 'bob' AND password = '".md5('bob123')."';
obvioulsy use variables for username and password, these queries are generated by php and then you can execute them on mysql
Please don't use MD5 for password hashing. Such passwords can be cracked in milliseconds. You're sure to be pwned by cybercriminals.
PHP offers a high-quality and future proof password hashing subsystem based on a reliable random salt and multiple rounds of Rijndael / AES encryption.
When a user first provides a password you can hash it like this:
$pass = 'whatever the user typed in';
$hashed_password = password_hash( "secret pass phrase", PASSWORD_DEFAULT );
Then, store $hashed_password in a varchar(255) column in MySQL. Later, when the user wants to log in, you can retrieve the hashed password from MySQL and compare it to the password the user offered to log in.
$pass = 'whatever the user typed in';
$hashed_password = 'what you retrieved from MySQL for this user';
if ( password_verify ( $pass , $hashed_password )) {
/* future proof the password */
if ( password_needs_rehash($hashed_password , PASSWORD_DEFAULT)) {
/* recreate the hash */
$rehashed_password = password_hash($pass, PASSWORD_DEFAULT );
/* store the rehashed password in MySQL */
}
/* password verified, let the user in */
}
else {
/* password not verified, tell the intruder to get lost */
}
How does this future-proofing work? Future releases of PHP will adapt to match faster and easier to crack encryption. If it's necessary to rehash passwords to make them harder to crack, the future implementation of the password_needs_rehash() function will detect that.
Don't reinvent the flat tire. Use professionally designed and vetted open source code for security.
Insertion:
INSERT INTO ... VALUES ('bob', MD5('bobspassword'));
retrieval:
SELECT ... FROM ... WHERE ... AND password=md5('hopefullybobspassword');
is how'd you'd do it directly in the queries. However, if your MySQL has query logging enabled, then the passwords' plaintext will get written out to this log. So... you'd want to do the MD5 conversion in your script, and then insert that resulting hash into the query.
PHP has a method called md5 ;-) Just $password = md5($passToEncrypt);
If you are searching in a SQL u can use a MySQL Method MD5() too....
SELECT * FROM user WHERE Password='. md5($password) .'
or
SELECT * FROM ser WHERE Password=MD5('. $password .')
To insert it u can do it the same way.
Why don't you use the MySQL built in password hasher:
http://dev.mysql.com/doc/refman/5.1/en/password-hashing.html
mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass') |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+
for comparison you could something like this:
select id from PassworTable where Userid='<userid>' and Password=PASSWORD('<password>')
and if it returns a value then the user is correct.
I'm not amazing at PHP, but I think this is what you do:
$password = md5($password)
and $password would be the $_POST['password'] or whatever
just get the hash by following line and store it into the database:
$encryptedValue = md5("YOUR STRING");
To increase security even more, You can have md5 encryption along with two different salt strings, one static salt defined in php file and then one more randomly generated unique salt for each password record.
Here is how you can generate salt, md5 string and store:
$unique_salt_string = hash('md5', microtime());
$password = hash('md5', $_POST['password'].'static_salt'.$unique_salt_string);
$query = "INSERT INTO users (username,password,salt) VALUES('bob','".$password."', '".$unique_salt_string."');
Now you have a static salt, which is valid for all your passwords, that is stored in the .php file. Then, at registration execution, you generate a unique hash for that specific password.
This all ends up with: two passwords that are spelled exactly the same, will have two different hashes. The unique hash is stored in the database along with the current id. If someone grab the database, they will have every single unique salt for every specific password. But what they don't have is your static salt, which make things a lot harder for every "hacker" out there.
This is how you check the validity of your password on login.php for example:
$user = //username input;
$db_query = mysql_query("SELECT salt FROM users WHERE username='$user'");
while($salt = mysql_fetch_array($db_query)) {
$password = hash('md5',$_POST['userpassword'].'static_salt'.$salt[salt]);
}
This method is very powerful and secure. If you want to use sha512 encryption, just to put that inside the hash function instead of md5 in above code.
Like most users, I'm simply trying to figure out a secure way to store passwords. What I haven't found here (or maybe it's my lack of understanding) is how to retrieve a salted hash in my database and separate the salt from the hashed password, especially with unique salts to each password while maintaining the salt+password in a single column.
I'm finding all these cool ways to encrypt passwords (SHA-256, but does MySQL only support SHA/1 and MD5?) and other things from the PHP manual, but not sure how store and retrieve the passwords.
So, far this is all I understand:
SHA('$salt'.'$password') // My query sends the password and salt
// (Should the $salt be a hash itself?)
After that I'm lost with salts.
Retrieving the password without a salt is easy, but the salt confuses me. Where do I get the value from $salt again, especially if it's unique and secure? Do I hide them in another database? Constant (seems unsafe)?
EDIT: Is the key variable in HMAC supposed to be salt or is this something else?
First of all, your DBMS (MySQL) does not need to have any support for cryptographic hashes. You can do all of that on the PHP side, and that's also what you should do.
If you want to store salt and hash in the same column you need to concatenate them.
// the plaintext password
$password = (string) $_GET['password'];
// you'll want better RNG in reality
// make sure number is 4 chars long
$salt = str_pad((string) rand(1, 1000), 4, '0', STR_PAD_LEFT);
// you may want to use more measures here too
// concatenate hash with salt
$user_password = sha512($password . $salt) . $salt;
Now, if you want to verify a password you do:
// the plaintext password
$password = (string) $_GET['password'];
// the hash from the db
$user_password = $row['user_password'];
// extract the salt
// just cut off the last 4 chars
$salt = substr($user_password, -4);
$hash = substr($user_password, 0, -4);
// verify
if (sha512($password . $salt) == $hash) {
echo 'match';
}
You might want to take a look at phpass, which also uses this technique. It is a PHP hashing solution which uses salting amongst some other things.
You should definitely take a look at the answer to the question WolfOdrade linked to.
Personally I recommend letting MySQL do this with its built in functions.
They way I do this is to create a function in my database config file which returns a key string. The config file should be outside your sites root so that the webserver can access the file but not others. so for example:
function enc_key(){
return "aXfDs0DgssATa023GSEpxV";
}
Then in your script use it with the sql query and AES_ENCRYPT and AES_DECRYPT functions in MySQL like this:
require_once('dbconf.inc.php');
$key = enc_key();
//When creating a new user
$sql = "INSERT INTO users (username, password) VALUES ('bob', AES_ENCRYPT('{$key}', {$password}))";
//When retrieving users password
$sql = "SELECT AES_DECRYPT('{$key}', password) AS password FROM users WHERE username like 'bob'";