如何生成好的盐 - 我的函数足够安全吗?

发布于 2024-09-30 19:38:15 字数 495 浏览 0 评论 0原文

这是我用来生成随机盐的函数:

function generateRandomString($nbLetters){
    $randString="";
    $charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for($i=0; $i<$nbLetters; $i++){
       $randInt=rand(0,61);
        $randChar=$charUniverse[$randInt];
        $randString=$randomString.$randChar;
    }
    return $randomString;
}

这​​是一个非商业网站。它仅用于生成盐(存储在数据库中并与用户提交的密码一起用于哈希)。

这合适吗?我应该使用更大的字符子集吗?如果是的话,在 PHP 中是否有一种简单的方法可以做到这一点?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

二货你真萌 2024-10-07 19:38:15

如果您要对密码进行哈希处理,则应该使用现代哈希算法,该算法不需要您生成自己的盐。使用弱哈希算法会给您和您的用户带来危险。我原来的答案是八年前写的。时代已经变了,密码散列现在变得容易多了。

您应该始终使用内置函数来散列/检查密码。任何时候使用自己的算法都会带来大量不必要的风险。

对于 PHP,请考虑使用 password_hash(),其中 PASSWORD_BCRYPT 算法。无需提供自己的盐。

以下是我的原始答案,供后代使用:


警告:根据 uniqid

php sha1 页面

$salt = uniqid(mt_rand(), true);

这看起来更简单,也更有效(因为每个都是独特)比你所提议的。

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.

阳光①夏 2024-10-07 19:38:15

如果您使用的是 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 库中内置的不同算法。

相关:https://security.stackexchange.com/questions/26206

相关:我应该使用 urandom 还是 openssl_random_pseudo_bytes?

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?

欢烬 2024-10-07 19:38:15

password_hash() 在 PHP 5.5 中可用和更新的。我很惊讶地发现这里没有提到它。

使用password_hash(),不需要生成盐,因为盐是使用bcrypt算法自动生成的——因此不需要组成一组字符。

相反,使用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()< /a>:

$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.

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.
萌能量女王 2024-10-07 19:38:15

rand(0,61) 替换为 < code>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;
}

有关散列拉伸的更多信息,请查看 这个答案...

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...

反目相谮 2024-10-07 19:38:15

我会从另一个答案中获取建议并使用 mt_rand(0, 61)< /a>,因为梅森扭曲器产生更好的熵。

此外,您的函数实际上由两部分组成:生成随机 $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;
}

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;
}
栖竹 2024-10-07 19:38:15

这是我的方法,它使用来自大气噪声的真正随机数。它全部与伪随机值和字符串混合在一起。打乱和散列。这是我的代码:我称之为矫枉过正。

<?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 提供。我还看到了通过色调和位置解释的熔岩灯图像的真正随机生成。 (色相是位置)

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)

眼前雾蒙蒙 2024-10-07 19:38:15

如果您有 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));
}

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));
}
云淡月浅 2024-10-07 19:38:15

我认为一个非常好的盐例如是用户名(如果您正在谈论 pw 哈希并且用户名不会改变)。

您不需要生成任何内容,也不需要存储更多数据。

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.

耶耶耶 2024-10-07 19:38:15

一种相当简单的技术:

$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2);  // or whatever sized salt is wanted

与 uniqid() 不同,它生成随机结果。

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.

只有影子陪我不离不弃 2024-10-07 19:38:15

我用这个:

$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));

I use this:

$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
自此以后,行同陌路 2024-10-07 19:38:15

如果您想要最终的唯一盐,您应该使用用户输入和要求的唯一值(例如电子邮件或用户名),然后使用 sha1 对其进行哈希处理,然后将其与代码生成的盐值合并(连接)。

另外,您必须通过一些特殊字符(例如 @,!#- 等)来扩展 $charUniverse

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文