Here's the function I'm using to generate random salts:
function generateRandomString($nbLetters){
$randString="";
$charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0; $i<$nbLetters; $i++){
$randInt=rand(0,61);
$randChar=$charUniverse[$randInt];
$randString=$randomString.$randChar;
}
return $randomString;
}
This is for a non commercial website. It's only used to generate the salt (to be stored in the db and used along with the user submitted pw for hashing).
Is this appropriate? Should I use a larger subset of characters, and if so is there an easy way to do that in PHP?
If you are hashing passwords, you should use a modern hashing algorithm that does not require you to generate your own salt. Using weak hashing algorithms presents a danger to both you and your users. My original answer was written eight years ago. Times have changed, and password hashing is a lot easier now.
You should always use built in functions to hash/check passwords. Using your own algorithms at any point introduces a huge amount of unnecessary risk.
For PHP, consider using password_hash(), with the PASSWORD_BCRYPT algorithm. There is no need to provide your own salt.
Below is my original answer, for posterity:
Warning: The following implementation does not produce an unpredictable salt, as per the documentation for uniqid.
From the php sha1 page:
$salt = uniqid(mt_rand(), true);
This looks simpler, and more effective (since each is unique) than what you have proposed.
If you're on Linux, /dev/urandom is probably your best source of randomness. It's supplied by the OS itself, so it's guaranteed to be much more reliable than any PHP built-in function.
$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);
This will give you 32 bytes of random blob. You'll probably want to pass this through something like base64_encode() to make it legible. No need to juggle characters yourself.
Edit 2014: In PHP 5.3 and above, openssl_random_pseudo_bytes() is the easiest way to get a bunch of random bytes. On *nix systems, it uses /dev/urandom behind the scenes. On Windows systems, it uses a different algorithm that is built into the OpenSSL library.
Related: https://security.stackexchange.com/questions/26206
Related: should i use urandom or openssl_random_pseudo_bytes?
password_hash() is availble in PHP 5.5 and newer. I am surprised to learn it is not mentioned here.
With password_hash() there is no need to generate a salt as the salt is automatically being generated using the bcrypt algorithm -- and therefore no need to make up a set of characters.
Instead, the user-submitted password is compared to the unique password hash stored in the database using password_verify(). Just store Username and Password hash in the user database table, you will then be able to compare it to a user-submitted password using password_verify().
How password hash()'ing works:
The password_hash() function outputs a unique password hash, when storing the string in a database -- it is recommended that the column allows up to 255 characters.
$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y
To verify a hashed password, you use password_verify():
$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE
If you prefer, salt can be added manually as an option, like so:
$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch#7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu <- This password hash not change.
Replace rand(0,61) with mt_rand(0, 61) and you should be fine (Since mt_rand is better at producing random numbers)...
But more important than strength of the salt is the way you hash it. If you have a great salt routine, but only do md5($pass.$salt), you're throwing away the salt. I personally recommend stretching the hash... For example:
function getSaltedHash($password, $salt) {
$hash = $password . $salt;
for ($i = 0; $i < 50; $i++) {
$hash = hash('sha512', $password . $hash . $salt);
}
return $hash;
}
For more information on hash stretching, check out this SO answer...
I would take advice from another answer and use mt_rand(0, 61), because the Mersenne Twister produces better entropy.
Additionally, your function is really two parts: generating random $nbLetters digits and encoding that in base62. This will make things much clearer to a maintenance programmer (maybe you!) who stumbles across it a few years down the road:
// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private function getBase62Char($num) {
return $chars[$num];
}
public function generateRandomString($nbLetters){
$randString="";
for($i=0; $i < $nbLetters; $i++){
$randChar = getBase62Char(mt_rand(0,61));
$randString .= $randChar;
}
return $randomString;
}
This is my method, It uses truly random numbers from atmospheric noise. It is all mixed in with pseudo-random values and strings. Shuffled and hashed. Here is my code: I call it overkill.
<?php
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
function get_true_random_number($min = 1, $max = 100) {
$max = ((int) $max >= 1) ? (int) $max : 100;
$min = ((int) $min < $max) ? (int) $min : 1;
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => '',
CURLOPT_USERAGENT => 'PHP',
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);
$ch = curl_init('http://www.random.org/integers/?num=1&min='
. $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
curl_close($ch);
if(is_numeric($content)) {
return trim($content);
} else {
return rand(-10,127);
}
}
function generateSalt() {
$string = generateRandomString(10);
$int = get_true_random_number(-2,123);
$shuffled_mixture = str_shuffle(Time().$int.$string);
return $salt = md5($shuffled_mixture);
}
echo generateSalt();
?>
The atmospheric noise is provided by random.org. I have also seen truly random generation from images of lava lamps that are interpreted via hue and location. (Hue is location)
Here is a much better way if you have windows and cant do /dev/random.
//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
I think that a very good salt for example is the user name (if you are talking about pw hashing and the user name doesn't change.)
You don't need to generate anything and don't need to store further data.
A fairly simple technique:
$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2); // or whatever sized salt is wanted
Unlike uniqid() it generates a random result.
I use this:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
If you want ultimate unique salt you should use a unique value entered and required by the user such as the email or the username, then hashing it using sha1 and then merge it - concatenate - with the salt value generated by your code.
Another, you have to extend $charUniverse by the mean of some special characters such as #,!#- etc.
Related
I'm trying to build a simple password generating function using openssl_random_pseudo_bytes and want to know if it is cryptographically strong.
function randomPassword($length = 12) {
mt_srand( hexdec( bin2hex( openssl_random_pseudo_bytes(256) ) ) );
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$&*-_=+.?';
$pass = '';
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < $length; $i++) {
$n = mt_rand(0, $alphaLength);
$pass .= $alphabet[$n];
}
return $pass;
}
I would assume that if there is a weakness that is is till in the mt_rand function regardless of seeding it with openssl_random_pseudo_bytes. However, I have been unable to find any discussion on the topic.
According to the documentation
openssl_random_pseudo_bytes is not guaranteed to produce cryptographically strong random data. However, in the improbable case that it was not able to use a cryptographically strong RNG, it will at least tell you. That's what openssl_random_pseudo_bytes's second parameter is for:
It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.
You can pass this parameter and then check its value:
$bytes = openssl_random_pseudo_bytes(256, $strong);
if (!$strong) {
// handle error as necessary.
}
However, opposed to openssl_random_pseudo_bytes, mt_rand is explicitly stated to not produce cryptographically secure values.
This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.
I'm no expert, but I doubt that seeding a Mersenne Twister with secure random data would make the generated data any more secure (if at all, probably just the first byte):
Counter-suggestion
You did not state which version of PHP you're using. If you are using PHP 7, random_int might make a good alternative for you (it's also explicitly stated to be cryptographically secure):
for ($i = 0; $i < $length; $i++) {
$n = random_int(0, $alphaLength);
$pass .= $alphabet[$n];
}
Alternatively, this answer provides a secure, PHP-based implementation for a rand function that uses OpenSSL as an CSPRNG that you could use in place of random_int.
Here's the function I'm using to generate random salts:
function generateRandomString($nbLetters){
$randString="";
$charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0; $i<$nbLetters; $i++){
$randInt=rand(0,61);
$randChar=$charUniverse[$randInt];
$randString=$randomString.$randChar;
}
return $randomString;
}
This is for a non commercial website. It's only used to generate the salt (to be stored in the db and used along with the user submitted pw for hashing).
Is this appropriate? Should I use a larger subset of characters, and if so is there an easy way to do that in PHP?
If you are hashing passwords, you should use a modern hashing algorithm that does not require you to generate your own salt. Using weak hashing algorithms presents a danger to both you and your users. My original answer was written eight years ago. Times have changed, and password hashing is a lot easier now.
You should always use built in functions to hash/check passwords. Using your own algorithms at any point introduces a huge amount of unnecessary risk.
For PHP, consider using password_hash(), with the PASSWORD_BCRYPT algorithm. There is no need to provide your own salt.
Below is my original answer, for posterity:
Warning: The following implementation does not produce an unpredictable salt, as per the documentation for uniqid.
From the php sha1 page:
$salt = uniqid(mt_rand(), true);
This looks simpler, and more effective (since each is unique) than what you have proposed.
If you're on Linux, /dev/urandom is probably your best source of randomness. It's supplied by the OS itself, so it's guaranteed to be much more reliable than any PHP built-in function.
$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);
This will give you 32 bytes of random blob. You'll probably want to pass this through something like base64_encode() to make it legible. No need to juggle characters yourself.
Edit 2014: In PHP 5.3 and above, openssl_random_pseudo_bytes() is the easiest way to get a bunch of random bytes. On *nix systems, it uses /dev/urandom behind the scenes. On Windows systems, it uses a different algorithm that is built into the OpenSSL library.
Related: https://security.stackexchange.com/questions/26206
Related: should i use urandom or openssl_random_pseudo_bytes?
password_hash() is availble in PHP 5.5 and newer. I am surprised to learn it is not mentioned here.
With password_hash() there is no need to generate a salt as the salt is automatically being generated using the bcrypt algorithm -- and therefore no need to make up a set of characters.
Instead, the user-submitted password is compared to the unique password hash stored in the database using password_verify(). Just store Username and Password hash in the user database table, you will then be able to compare it to a user-submitted password using password_verify().
How password hash()'ing works:
The password_hash() function outputs a unique password hash, when storing the string in a database -- it is recommended that the column allows up to 255 characters.
$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y
To verify a hashed password, you use password_verify():
$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE
If you prefer, salt can be added manually as an option, like so:
$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch#7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu <- This password hash not change.
Replace rand(0,61) with mt_rand(0, 61) and you should be fine (Since mt_rand is better at producing random numbers)...
But more important than strength of the salt is the way you hash it. If you have a great salt routine, but only do md5($pass.$salt), you're throwing away the salt. I personally recommend stretching the hash... For example:
function getSaltedHash($password, $salt) {
$hash = $password . $salt;
for ($i = 0; $i < 50; $i++) {
$hash = hash('sha512', $password . $hash . $salt);
}
return $hash;
}
For more information on hash stretching, check out this SO answer...
I would take advice from another answer and use mt_rand(0, 61), because the Mersenne Twister produces better entropy.
Additionally, your function is really two parts: generating random $nbLetters digits and encoding that in base62. This will make things much clearer to a maintenance programmer (maybe you!) who stumbles across it a few years down the road:
// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private function getBase62Char($num) {
return $chars[$num];
}
public function generateRandomString($nbLetters){
$randString="";
for($i=0; $i < $nbLetters; $i++){
$randChar = getBase62Char(mt_rand(0,61));
$randString .= $randChar;
}
return $randomString;
}
This is my method, It uses truly random numbers from atmospheric noise. It is all mixed in with pseudo-random values and strings. Shuffled and hashed. Here is my code: I call it overkill.
<?php
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
function get_true_random_number($min = 1, $max = 100) {
$max = ((int) $max >= 1) ? (int) $max : 100;
$min = ((int) $min < $max) ? (int) $min : 1;
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => '',
CURLOPT_USERAGENT => 'PHP',
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);
$ch = curl_init('http://www.random.org/integers/?num=1&min='
. $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
curl_close($ch);
if(is_numeric($content)) {
return trim($content);
} else {
return rand(-10,127);
}
}
function generateSalt() {
$string = generateRandomString(10);
$int = get_true_random_number(-2,123);
$shuffled_mixture = str_shuffle(Time().$int.$string);
return $salt = md5($shuffled_mixture);
}
echo generateSalt();
?>
The atmospheric noise is provided by random.org. I have also seen truly random generation from images of lava lamps that are interpreted via hue and location. (Hue is location)
Here is a much better way if you have windows and cant do /dev/random.
//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
I think that a very good salt for example is the user name (if you are talking about pw hashing and the user name doesn't change.)
You don't need to generate anything and don't need to store further data.
A fairly simple technique:
$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2); // or whatever sized salt is wanted
Unlike uniqid() it generates a random result.
I use this:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
If you want ultimate unique salt you should use a unique value entered and required by the user such as the email or the username, then hashing it using sha1 and then merge it - concatenate - with the salt value generated by your code.
Another, you have to extend $charUniverse by the mean of some special characters such as #,!#- etc.
I'm building a login system and I want to be sure I'm writing the write code to generate and store passwords in the db. $options['passwd'] is the string selected as a password by the user.
This is my code to generate a hash:
public function createPasswd($options) {
$hash_seed = 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329';
$password = $options['passwd'];
$date = new DateTime();
$timestamp = $date->getTimestamp();
$rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3));
$rand = pow($rand_number, 12);
$salt = str_shuffle($rand.$hash_seed);
$hash = crypt($password, $salt);
return $hash;
}//End class createPasswd
I just store the hash on the database and then compare it with user's password like the following:
if ($hash == crypt($password, $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
Is this strong enough? Am I missing some big issue?.
Ok, let’s see:
You’re generating a number between the current timestamp (≥ 1393631056) and something around 7.59E+59:
$rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3));
$rand = pow($rand_number, 12);
These values and the calculation seems to be just arbitrary. Frankly, if the random value is already large, chances are that pow($rand_number, 12) returns INF.
Then you put the random number and the fixed seed, shuffle it, and use it as salt with crypt to hash the password:
$salt = str_shuffle($rand.$hash_seed);
$hash = crypt($password, $salt);
The random float number plus the fixed salt yield only between 16 (in case of INF) and 20 different characters. However, looking closer at crypt reveals that if you don’t specify the algorithm, crypt will use CRYPT_STD_DES:
Standard DES-based hash with a two character salt from the alphabet "./0-9A-Za-z". Using invalid characters in the salt will cause crypt() to fail.
Now there are two aspects in these sentences that should make you suspicious:
Standard DES-based does only use two characters as salt, and
these characters must be from ./0-9A-Za-z, otherwise crypt fails.
So your salt does not only contain characters other than ./0-9A-Za-z, but with the at worst 16 characters large character set there are only 16^2 = 256 possible salts.
So, what should you do? Just don’t try to reinvent the wheel but use existing and proven solutions.
Is this strong enough? Am I missing some big issue?
Modern PHP actually provides exceptionally good password hashing built in - BCrypt, one of the three (SCrypt, BCrypt, PBKDF2) consistently recommended password hashing functions (as of early 2014). Even better, it handles salting for you (salt should simply be random and long enough).
If you're on PHP 5.5 or later, please read Safe Password Hashing at PHP.net - this is the FAQ for storing passwords in PHP. If you are on PHP 5.3.7 but not yet 5.5, you can use the password_compat library to use these functions.
These functions use BCrypt and handle the salt for you, so it's easy!
In particular, you can hash the password with a high enough cost (pick a cost that takes just long enough that under your expected maximum load, your site will be not quite CPU bound) - like in password_hash Example 2:
<?php
/**
* In this case, we want to increase the default cost for BCRYPT to 12.
* Note that we also switched to BCRYPT, which will always be 60 characters.
*/
$options = [
'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>
Then you store the string it returns.
To verify, retrieve the string it returned from wherever you stored it (i.e. your database) and compare with the password_verify example:
<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
?>
As always, if you want details, please read How to securely hash passwords? - but the PHP 5.5 password functions use Bcrypt, so you can just use a high enough cost.
Note that as time goes on and you buy better hardware, you should increase the cost to match. You can transparently do that by, after verifying the password at the old cost, checking for the old cost and if found, rehashing it with a new cost as part of the login process, so you can increase the security of the stored passwords without bothering your users. Of course, users who never log in don't get the benefits of this.
For the old pre-5.3.7 crypt() example, see the leading answer to How do you use bcrypt for hashing passwords in PHP?, which has a getSalt function of:
private function getSalt() {
$salt = sprintf('$2a$%02d$', $this->rounds);
$bytes = $this->getRandomBytes(16);
$salt .= $this->encodeBytes($bytes);
return $salt;
}
Longer salt doesn't mean better protection. You don't use crypt function properly. $salt argument should not be a simple random string.
Consider this exemple :
echo crypt('password one', 'salt lorem ipsum dolor sit amet');
echo crypt('password two', 'salt');
Both will return the same string ! (sa3tHJ3/KuYvI)
Check http://php.net/crypt for more information about how to use $salt the correct way.
It's also much better (safer?) to keep an unique hash_seed code side and then store in the database only a sha hash (or other algo) of a string combining the password and your hash_seed.
Correct implementation would be :
define('SECRET_KEY', 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329'); // longer is better
public function createPasswd($options) {
return hash('sha256', SECRET_KEY . $options['passwd']);
}
To check the password :
if ($stored_hash == hash('sha256', SECRET_KEY . $password) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
sha256 can be replaced with any available algorithms on your system. Get the complete list with :
var_dump(hash_algos());
I'm sure you're overdoing that all.
It's pretty enough if you will use something like
$salt = get_random_salt(); // will return any random string
$hash = md5(md5($salt).md5($user_password)); // or any combinations of hashing and concatenations. It's your secure alorithm. Or use other more resistant hash algorithm.
Than you need to save $hash and $salt into DB and check in same manner:
$hash = md5(md5($salt_from_db).md5($user_password));
if ($hash == $hash_from_db) {
// success
} else {
// fail
}
Also in your example you're doing every time operation pow(91239193912939123912,3) but it will return same result every time. So it's the same if you will add just pre-calculated value.
I mean it's not necessary to have that complicated salt algorithm. You can just use mt_rand() for that purpose.
Also see here: Secure hash and salt for PHP passwords
Surfing the web I found a better solution that uses blowfish.
With this approach I understand I've only have to store the hash on the database, not the salt. Obviously the more rounds the stronger password and slower generation.
public function better_crypt($input, $rounds = 8) {
$salt = "";
$salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9));
for($i=0; $i < 22; $i++) {
$salt .= $salt_chars[array_rand($salt_chars)];
}
return crypt($input, sprintf('$2a$%02d$', $rounds) . $salt);
}//End function
if(crypt($pass_string, $passwd) == $passwd) {
echo 'password is correct';
}
}
Use password_hash() which utilises bcrypt under the hood by default: http://ie2.php.net/password_hash
If you can't use it from your PHP version, there's: https://github.com/ircmaxell/password_compat
If you are using anything else, attempting to write it yourself, stop now and please don't listen to the idiots suggesting any other option. Especially when they suggest MD5...
(Updated due to some confusion over my question) We use this method to generate a digested_password, which is saved to our database for later lookup. When we authenticate a user to our application, this method is again ran against their input and if the digested_password output matches the digested_password we saved in the database we authenticate them. I need to reproduce this function in PHP for another application that shares the database.
def self.hash_password( password, salt )
salted_password = password.insert 4, salt
digested_password = Digest::SHA512.hexdigest("#{salted_password}")
return digested_password
end
I really have no experience with Ruby so please forgive my lack of understanding.
Thanks!
Because you can't "retrieve" a password from a one-way hash like SHA-512, the closest we can get is being able to generate the same hash from the same password and salt. Let's look at the Ruby code:
def self.hash_password( password, salt )
salted_password = password.insert 4, salt
digested_password = Digest::SHA512.hexdigest("#{salted_password}")
return digested_password
end
The only thing a bit "weird" here is the call to the .insert method on the password string. It's basically splicing the salt right into the password, starting at the fourth character index. Normally salts are simply concatenated with the password.
We can replicate this using substr:
function hash_password($password, $salt) {
$salted_password = substr($password, 0, 4) . $salt . substr($password, 4);
return hash('sha512', $salted_password);
}
I'm using the now-default hash extension here, as it's pretty much the most reliable way to generate a SHA-512 hash.
Using this code, you should be able to generate identical hashes for a given identical password and salt combination. I'm not sure what the behavior would be when the password is less than five characters long.
Yup, looks like it should work:
[charles#lobotomy ~]$ irb
irb(main):001:0> require 'digest/sha2'
=> true
irb(main):002:0> def hash_password( password, salt )
irb(main):003:1> salted_password = password.insert 4, salt
irb(main):004:1> digested_password = Digest::SHA512.hexdigest("#{salted_password}")
irb(main):005:1> return digested_password
irb(main):006:1> end
=> nil
irb(main):007:0* puts hash_password('password', 'salt')
92d3efdbf51d199b0930c427b77dc8d5cf41ac58b6fab5f89cc3f32d719a8f6ffcdff6211bdd0565a6e7b09925839e5dcce1fa5abf65eca87c6a883ab0b510b9
=> nil
irb(main):018:0> exit
[charles#lobotomy ~]$
[charles#lobotomy ~]$ php -a
Interactive shell
php > function hash_password($password, $salt) {
php { $salted_password = substr($password, 0, 4) . $salt . substr($password, 4);
php { return hash('sha512', $salted_password);
php { }
php > echo hash_password('password', 'salt');
92d3efdbf51d199b0930c427b77dc8d5cf41ac58b6fab5f89cc3f32d719a8f6ffcdff6211bdd0565a6e7b09925839e5dcce1fa5abf65eca87c6a883ab0b510b9
php >
function hash_password($password, $salt)
{
$salted_password = substr_replace($password, $salt, 4, 0);
$digested_password = hash('sha512', $salted_password);
return $digested_password;
}
SHA512 is a one way hash function, meaning that you can't easily get the input for any given output.
It is possible, but very expensive, to build a rainbow table. Because this is a salted hash, it's even more difficult
I figured it out. I didn't quite understand the insert method in Ruby, and it turns out the salt is being inserted into the password at the fourth character. Here is the PHP equivalent:
$password_input = "derpaderp";
$password_salt = "aderp";
$arg=substr_replace($password_input, $password_salt, 4, 0);
$pass = hash('sha512', $arg);
if ( $digested_password_from_database == $pass ){ echo "success!"; }
Thanks for your replies folks.
After reading about salts password hashing Id like to implement a simple version for an admin area to a site Im building.
If you have any good links with code that have implemented this idea well, I would appreciate it if you could share.
Thanks,
Registration process: User enters a password. System generates a salt value from random data (could be a hash of the time & PID or something). Systems generates a hash value of the password & salt value and stores both of these in the registration table.
Login process: User enters a password. System pulls the salt value from the database and hashes it and the password and compares that against the hashed password value put into the database during registration.
The plaintext password is never stored in the database. The salt value is never visible to the client.
Well, here's what I would do:
function makeToken($length = 16) {
if ($length > 16) {
$ret = '';
while ($length > 0) {
$ret .= makeToken(16);
$length -= 16;
}
if ($length < 0) {
$ret = substr($ret, 0, $length);
}
return $ret;
}
$stub = '';
for ($i = 0; $i < 100; $i++) {
$stub .= chr(mt_rand(1, 254));
}
$hash = sha1($stub);
$hashLen = strlen($hash);
$ret = '';
for ($i = 0; $i < $length; $i++) {
$ret .= $hash[mt_rand(0, $hashLen - 1)];
}
return $ret;
}
function makeSaltedHash($string, $salt = '') {
if (empty($salt)) {
$salt = makeToken();
}
$hash = '';
for ($i = 0; $i < 200; $i++) {
$hash = sha1($hash . $salt . $string);
}
return $hash . ':' . $salt;
}
function verifySaltedHash($string, $hash) {
if (strpos($string, ':') === false) return false;
list ($base, $salt) = explode(':', $hash);
$test = makeSaltedHash($string, $salt);
return $test === $hash;
}
The rational is this:
First, generate a random salt (this will always return a hex string, so it can be used for tokens etc). Then loop over a hashing function (sha1 in this case) more than 1 time. This is so that it becomes more expensive to generate a rainbow table (200x as expensive in this case) while adding relatively little expense to generation.
Usage:
To generate a hash:
$hash = makeSaltedHash($password);
To verify the hash:
$bool = verifySaltedHash($password, $savedHash);
To generate a token (CSRF protection, session_id, etc), you can do it a few ways:
Fixed Length:
$token = makeToken(32);
Random Length:
$token = makeToken(mt_rand(64,128));
Edit: Increased the repetitions on sha1 in the hashing function.
function encodePwd($salt, $string) {
return sha1( $salt . $string );
}
think about salt randomization for a minute though. Password encoding specifically.
If i have salt of "random" and a password of "complex", my sha1 would be
e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
that's stored in the database. I want to check against that.
So when a user supplies me "complex" as a password, i tag "random" in front of it and encode it to get the same hash. If they equal, then bazinga! i'm set.
But what if that was random?
salt when it was stored: "random"
SHA1: e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
salt when the user put it in: "apple"
SHA1: e07b207d77a0bd27d321552fc934b186559f9f42
how am i going to match those?
If you are looking for a more secure method, use data that you have and that is constant like the username or id of user or something (preferably something that won't change). You need a pattern you can rely on.
username would work good (you'd have to make sure to update password if they ever changed the username) that way authentication could look like
`WHERE `username` = '&username' AND `password` = '" . encodePwd( $username, $password ) . "'"`
function encodePwd( $username, $password) {
// maybe modify username on a non-random basis? - like
// $username = sha1( substr($username, 2)); // assuming usernames have a min-length requirement
return sha1( $username . $password ) ;
}
I don't have a link to available code, but what I've done in the past is to generate a randomized salt - $salt = rand(1,1000000000); - and save it in a session. I pass that salt to a login page and then use JavaScript to create a SHA hash of the salt + password which is submitted rather than a plaintext password. Since the salt is stored in the session I can then use that to see if the login hash matches the salt + password hash stored in the db.
If you need really secure hashes, please use the Portable PHP hashing framework.
I'd also recommend this Month of PHP security article that deals extensively with password hashing and the security of hashes.
There are so many ways you can create a salt string, but i think you don't need to think a lot about your salt strength.
I hash passwords like this
$hash = sha1(strlen($password) . md5($password) . $salt);
I think its the best performance between speed, and "security".
function salt($lenght = 9) {
$numbers = '0123456789';
$chars = 'qwertzuiopasdfghjklyxcvbnm';
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {
if ($alt == 1)
{
$password .= $chars[(rand() % strlen($chars))];
$alt = 0;
} else
{
$password .= $numbers[(rand() % strlen($numbers))];
$alt = 1;
}
}
return $password;
}
the code is simple, and dan heberden has already provided it.
a salt is simply a piece of text that you append or prepend to a password before generating a hash. eg, if your password is 'password' and the salt is 'salt' then the hash will be hashFunction('saltpassword') instead of hashFunction('password').
salts are generally used to avoid rainbow password cracks - this is where a large list of passwords and their hashes are checked against the hashed password. eg in the above example, say there is a hash 123456 which corresponds to hashFunction('password'), if the attacker knows your hash is 123456 then they know your password is 'password'.
your salt should be a random string of letters and numbers - eg kjah!!sdf986. it's very very unlikely for someone to have a rainbow table including kjah!!sdf986password so even if someone gets your hashed password then it's kinda useless.
however, you obviously need to use the same salt every time, or at least store the salt as well as the password. because if you pick a random salt every time chances are your hashed salt+password will not be the same :D
Could you use the built in crypt(...) function?
... Usage ...
crypt('rasmuslerdorf', '$1$rasmusle$')
... Result ...
MD5: $1$rasmusle$rISCgZzpwk3UhDidwXvin0
... There are more examples and other hash methods available in the documentation.