This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
will this function be safe for password and email hash/crypt? EDIT: Cleary not!
$password = mysql_real_escape_string(htmlspecialchars(trim($_POST['password'])));
$hash_algo = "sha512";
$raw_output = false;
$hash = hash($hash_algo, $password, $raw_output);
$hash_20 = substr($hash, 0, 20);
$salt = substr($hash, -20);
$crypt = crypt ( $hash_20, $salt);
$crypt_20 = substr($crypt, 0, 20);
EDIT:
Here is the code I'm using now. I think this one is pretty safe. It's a PBKDF2 password hash function with a random salt generator.
So, here is the PBKDF2 function.
p is for password.
s is for salt.
c is for iteration
kl is for key lenght.
a is for hash algorithm.
function pbkdf2( $p, $s, $c, $kl, $a = 'sha256' )
{
$hl = strlen(hash($a, null, true)); # Hash length
$kb = ceil($kl / $hl); # Key blocks to compute
$dk = ''; # Derived key
# Create key
for ( $block = 1; $block <= $kb; $block ++ ) {
# Initial hash for this block
$ib = $b = hash_hmac($a, $s . pack('N', $block), $p, true);
# Perform block iterations
for ( $i = 1; $i < $c; $i ++ )
# XOR each iterate
$ib ^= ($b = hash_hmac($a, $b, $p, true));
$dk .= $ib; # Append iterated block
}
# Return derived key of correct length
return substr($dk, 0, $kl);
}
Salt generator:
function salt( $length )
{
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$salt="";
$size = strlen( $chars );
for( $i = 0; $i < $length; $i++ )
{
$salt.= $chars[ rand( 0, $size - 1 ) ];
}
return $salt;
}
In use:
if(isset($_POST['submit']))
{
$Password = mysql_real_escape_string(htmlspecialchars(trim($_POST['Password'])));
//To make sure the salt has never more chars than the password.
$salt_length = strlen($Password);
$salt = salt($salt_length);
//Hash Password
$hash = base64_encode(pbkdf2($Password, $salt, 100000, 32));
//--------------//
}
Googling a bit find out that 100000 iterations is pretty safe but I guess 10000 will be enough tho.
Since you're hashing the input, you cannot simply reverse it to the original value. Assuming an attacker knows this algorithm, the question is how long does it take to brute force the password. For that, test how long one iteration of the algorithm takes. Then calculate how many tries an attacker would have to do to try all possible passwords on a high-end machine. Then you have your answer how "safe" the algorithm is. You are looking for an answer measured at least in millennia, but preferably big bangs.
That is, assuming there are no actual attacks against the algorithm an attacker could try that would shorten that time.
Since you are deriving the salt from the input itself, you're simply stretching the algorithm a bit. You're not using an actual salt, which is a random unique value that is independent of the input. As such, you are using an unsalted input with a not so complicated hashing algorithm. My bet would be that it's not very hard to brute force a whole database of passwords "secured" with this algorithm.
Related
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.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I'm looking to run a small raffle but i'd like people to be able to verify that the number chosen was fair. I know some sites use a "Provably Fair" system to achieve this where user input is combined with a secret string which is then hashed to determine a winning number. Once the secret string is released, participants can verify using the hash that the correct winner was chosen.
From a sha512 hash a "random" number from 0-n (n varies in length but is 255 at most) should be generated to determine the winner. This is what I thought up:
Creating an array of n length and inserting pairs of characters that can appear in a sha hash. If n was 255 for example, the array would have: array('aa','ab','ac'...'fd','fe','ff'...'97','98','99'). Basically it'd have every combination of two characters using a-f and 0-9.
It'd then look at the first two characters of the hash and use array_search to see if that pair exists in the array. If it does, it's index is the number picked, otherwise it moves over to the next pair of characters and searches those. In the rare scenario it doesn't find a match it'll continually hash itself and use the new hash returned to continue the search.
$values = array('a','b','c','d','e','f','0','1','2','3','4','5','6','7','8','9');
$numbers = array();
$ENTRANTS = 50;
$rand = md5(uniqid(rand(), true)); //This would instead be $secret . $user_input
$hash = hash('sha512',$rand);
$values_index = 0;
$current = $values[$values_index];
$options = 0;
while ($options < $ENTRANTS){
$remaining = ($ENTRANTS - $options > 15)? 16: $ENTRANTS-$options;
for ($n=0; $n < $remaining ;$n++){
array_push($numbers, $current . $values[$n]);
$options++;
}
$values_index++;
$current = $values[$values_index];
}
$outcomes = array();
$winning = null;
$i = 0;
while (empty($winning)){
while ($i+1 < 64 && empty($winning)){
$combo = $hash[$i] . $hash[$i+1];
$number = array_search($combo,$numbers);
if ($number !== false){
$winning = $number;
}
$i++;
}
if (!empty($winning)){
echo "<pre>" . print_r($numbers,true) . "</pre>";
echo $hash . "<br>" . $winning;
}
else {
echo "re-hashing" . "<br>";
$hash = hash("sha512",$hash);
$i = 0;
}
}
This is working when tested but I'm unsure if it is effectively generating a random number. Are the characters in a sha512 hash for the most part evenly and unpredictably distributed? If you can see any issues with this or have any advice on how it could be improved please let me know!
The best way - as always in crypto - is not to roll your own security if it can be avoided. As your random number generator rand() is not cryptographically secure (see the Notes section). Applying a hash on it won't make this random number generator secure. Actually, it is as good as impossible to make a good RNG from a (set of) bad ones.
Instead it is much better to use openssl_random_pseudo_bytes. There is no need to perform any post processing on the bytes you get from that random number generator - at least not to add cryptographic strength.
I have been trying to make my users passwords really secure using pbkdf2.
The password hash goes into the database fine, but the salt is not.
It seems the salt contains exotic characters that the mysql column doesnt like.
All columns in my 'users' table are UTF8_unicode_ci.
Here is my password hasher:
$size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB);
$salt = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM);
$passHash = pbkdf2('SHA512', $pass, $salt, 8192, 256) ;
include("dbconnect.php") ;
$result = $dbh->prepare("INSERT INTO users (name, email, qq, password, salt)VALUES(?, ?, ?, ?, ?)") ;
$result->bindParam(1, $name, PDO::PARAM_STR) ;
$result->bindParam(2, $email, PDO::PARAM_STR) ;
$result->bindParam(3, $qq, PDO::PARAM_STR) ;
$result->bindParam(4, $passHash, PDO::PARAM_STR) ;
$result->bindParam(5, $salt, PDO::PARAM_STR) ;
$result->execute() ;
And the pbkdf2:
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false){
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
Also, I have just noticed that it is storing totally different hashes for passwords that are the same.
Am I doing this right?
It's hard to give an exact answer to a question as broad as 'Am I doing this right?' I can say that the entire point of salting passwords before hashing is so that the resultant hash will be unique between users. So getting different output for the same input is a good thing. Look up 'dictionary attack' for more information on why.
As to your code, it sounds like what you really want to know is why your salt isn't getting stored to the database. Debugging steps I can think of, without more specific details
$salt could be false, mcrypt_create_iv returns false on error (unlikely because of the hash output differences you mentioned above, but worth checking.
Output characters are not recognized as you suspect. You could try converting the database column types to varbinary and using a string to binary or hex decoder before adding to your prepare.
Try experimenting with column types with different character encodings and see what column type your salts can go into. UTF-8 uses a variable number of bytes for each character, which makes me uncomfortable when dealing with things that are absolutes. A salt and a hash are generally considered to be fixed-with bit fields, often expressed in hex format for convenience.
I might be able to narrow down a problem if you provided your server environment, php version, mysql version etc. and a few samples of salts which aren't being stored correctly.
You should convert the result into base64 encoding before storing to a varchar column. Base64 encoding basically converts an array of bytes into something in the ASCII range of displayable (and therefore SQL storable) characters.
I'm trying to fix things on a PHP site. There is a pair of PHP functions:
function get_rnd_iv($iv_len) {
$iv = '';
while ($iv_len-- > 0) {
$iv .= chr(mt_rand() & 0xff);
}
return $iv;
}
function md5_encrypt($plain_text, $password, $iv_len = 16) {
$plain_text .= "\x13";
$n = strlen($plain_text);
if ($n % 16) $plain_text .= str_repeat("\0", 16 - ($n % 16));
$i = 0;
$enc_text = get_rnd_iv($iv_len);
$iv = substr($password ^ $enc_text, 0, 512);
while ($i < $n) {
$block = substr($plain_text, $i, 16) ^ pack('H*', md5($iv));
$enc_text .= $block;
$iv = substr($block . $iv, 0, 512) ^ $password;
$i += 16;
}
return base64_encode($enc_text);
}
function md5_decrypt($enc_text, $password, $iv_len = 16) {
$enc_text = base64_decode($enc_text);
$n = strlen($enc_text);
$i = $iv_len;
$plain_text = '';
$iv = substr($password ^ substr($enc_text, 0, $iv_len), 0, 512);
while ($i < $n) {
$block = substr($enc_text, $i, 16);
$plain_text .= $block ^ pack('H*', md5($iv));
$iv = substr($block . $iv, 0, 512) ^ $password;
$i += 16;
}
return preg_replace('/\\x13\\x00*$/', '', $plain_text);
}
They are used to encrypt the IP addresses of users in the database.
The $password parameter is stored in a php config file (so only dumping the sql will not give you the IP-s even when you know these functions).
I'm still puzzled over them as MD5 is clearly hashing and only things like bruteforcing can reverse it.
Can someone with more php experience explain, how this decryption works? The encrypted text is not a simple MD5 so I might have to understand what is happening there.
Anyway I'm trying to write a mysql stored function doing the decryption, because I want to join an other table by the IP-s (a table containing IP ranges for countries), and only return the country codes in the query.
The problems are: I have never written a MySQL function. How do I make a while cycle? there are functions like pack, and preg_replace which are not built in in MySQL, do I have to implement them too somehow?
Any help would be appreciated (ranging from hints to the full function)!
Comments like "MD5 cannot be decrypted it's hashing!" will not be appreciated.
In these functions md5 is only used to compute hash of constant component $iv_len, that is later used like salt to the passwords.
Every other operations are reversible (string padding, packing and XOR [^])
As for MySQL function, I wouldn't do it. IMHO better solution is to extend users table with country_from_ip column and populate it from php, by decrypting all existing IPs and getting their country code and modifying php code to add this info while saving new record.
Making a join on field computed by function will very soon become bottleneck for your application
The MD5 hash isn't performed on the IP address, it appears to be used to add some random seed data to the encryption, or something.
As for how to decrypt it, why do you want to use SQL? While is probably possible, I suspect PHP is much faster at this type of operation.
Use SQL to select, say, 1000 user rows at a time, and then use PHP to actually link them against each country.
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.