可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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?
回答3:
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.
回答4:
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...
回答5:
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;
}
回答6:
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)
回答7:
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));
}
回答8:
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.
回答9:
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.
回答10:
I use this:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
回答11:
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.