这是我用来生成随机盐的函数:
function generateRandomString($nbLetters){
$randString="";
$charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0; $i<$nbLetters; $i++){
$randInt=rand(0,61);
$randChar=$charUniverse[$randInt];
$randString=$randomString.$randChar;
}
return $randomString;
}
这适用于非商业网站。它仅用于生成salt(存储在db中并与用户提交的pw一起用于散列)。
这是否合适?我应该使用更大的字符子集,如果是这样,在PHP中有一种简单的方法吗?
答案 0 :(得分:40)
如果您正在哈希密码,则应使用现代哈希算法,该算法不需要您生成自己的盐。使用弱哈希算法会对您和您的用户造成危险。我的原始答案是八年前写的。时代已经改变,密码散列现在变得容易多了。
您应该始终使用内置函数来散列/检查密码。在任何时候使用自己的算法都会带来大量不必要的风险。
对于PHP,请考虑使用password_hash()和PASSWORD_BCRYPT
算法。没有必要提供自己的盐。
以下是我的原始答案,后人:
警告:根据uniqid的文档,以下实现不会产生不可预测的salt。
$salt = uniqid(mt_rand(), true);
这看起来比你提出的更简单,更有效(因为每个都是唯一的)。
答案 1 :(得分:18)
如果你在Linux上,/dev/urandom
可能是你最好的随机来源。它由操作系统本身提供,因此它保证比任何PHP内置函数更可靠。
$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);
这将为您提供32个字节的随机blob。您可能希望通过类似base64_encode()
之类的内容传递此信息,以使其清晰可辨。不需要自己玩弄角色。
编辑2014:在PHP 5.3及更高版本中,openssl_random_pseudo_bytes()
是获取大量随机字节的最简单方法。在* nix系统上,它在幕后使用/dev/urandom
。在Windows系统上,它使用内置于OpenSSL库中的不同算法。
答案 2 :(得分:8)
password_hash()
可在PHP 5.5及更高版本中使用。我很惊讶地发现这里没有提到它。
使用password_hash()不需要生成salt,因为使用bcrypt算法自动生成salt - 因此无需编写一组字符。
相反,使用password_verify()将用户提交的密码与存储在数据库中的唯一密码哈希进行比较。只需将用户名和密码哈希存储在用户数据库表中,您就可以使用password_verify()将其与用户提交的密码进行比较。
密码hash()的工作方式:
当将字符串存储在数据库中时,password_hash()函数输出唯一的密码哈希值 - 建议该列最多允许255个字符。
$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
要验证哈希密码,请使用password_verify()
:
$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE
如果您愿意,可以手动添加盐作为选项,如下所示:
$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch@7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu <- This password hash not change.
答案 3 :(得分:4)
将rand(0,61)
替换为mt_rand(0, 61)
,您应该没问题(因为mt_rand
更适合生成随机数)...
但是比盐的强度更重要的是你散列它的方式。如果你有一个很好的盐例程,但只做md5($pass.$salt)
,你就扔掉了盐。我个人建议拉伸哈希...例如:
function getSaltedHash($password, $salt) {
$hash = $password . $salt;
for ($i = 0; $i < 50; $i++) {
$hash = hash('sha512', $password . $hash . $salt);
}
return $hash;
}
有关散列拉伸的更多信息,请查看this SO answer ...
答案 4 :(得分:4)
我会从另一个答案中获取建议并使用mt_rand(0, 61),因为Mersenne Twister会产生更好的熵。
此外,您的功能实际上是两部分:生成随机$nbLetters
数字并在base62中编码。这将使维护程序员(也许是你!)的事情变得更加清晰,他们在未来几年偶然发现它:
// 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;
}
答案 5 :(得分:2)
这是我的方法,它使用来自大气噪声的真正随机数。它全部与伪随机值和字符串混合在一起。洗牌和散打。这是我的代码:我称之为矫枉过正。
<?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();
?>
大气噪声由random.org提供。我也看到了通过色调和位置解释的熔岩灯图像的真正随机生成。 (顺化是位置)
答案 6 :(得分:2)
如果你有windows并且不能/ 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));
}
答案 7 :(得分:1)
我认为一个非常好的盐就是用户名(如果你在谈论pw哈希并且用户名不会改变。)
您无需生成任何内容,也无需存储更多数据。
答案 8 :(得分:1)
一种相当简单的技术:
$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2); // or whatever sized salt is wanted
与uniqid()不同,它会生成随机结果。
答案 9 :(得分:1)
我用这个:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
答案 10 :(得分:0)
如果您想要最终的独特盐,您应该使用用户输入和要求的唯一值,例如电子邮件或用户名,然后使用sha1对其进行散列,然后将其合并 - 连接 - 使用您的代码生成的salt值。
另外,您必须将$charUniverse
扩展为@,!#-
等某些特殊字符的平均值。