php 如何生成好的盐 - 我的功能是否足够安全?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4099333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 11:56:58  来源:igfitidea点击:

How to generate a good salt - Is my function secure enough?

phpsecuritysalt

提问by JDelage

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?

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

回答by fredley

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 alwaysuse 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_BCRYPTalgorithm. There is no need to provide your own salt.

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

Below is my original answer, for posterity:

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



Warning: The following implementation does not produce an unpredictable salt, as per the documentation for uniqid.

警告:根据uniqid的文档,以下实现不会产生不可预测的盐。

From the php sha1 page:

php sha1 页面

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

This looks simpler, and more effective (since each is unique) than what you have proposed.

这看起来比您提出的更简单,更有效(因为每个都是独一无二的)。

回答by kijin

If you're on Linux, /dev/urandomis 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.

如果您使用的是 Linux,/dev/urandom这可能是您最好的随机性来源。它由操作系统本身提供,因此保证比任何 PHP 内置函数更可靠。

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

这将为您提供 32 字节的随机 blob。您可能希望通过诸如base64_encode()使其清晰之类的东西来传递它。无需自己处理角色。

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/urandombehind the scenes. On Windows systems, it uses a different algorithm that is built into the OpenSSL library.

2014编辑:在 PHP 5.3 及更高版本中,openssl_random_pseudo_bytes()是获取一堆随机字节的最简单方法。在 *nix 系统上,它/dev/urandom在幕后使用。在 Windows 系统上,它使用内置在 OpenSSL 库中的不同算法。

Related: https://security.stackexchange.com/questions/26206

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

Related: should i use urandom or openssl_random_pseudo_bytes?

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

回答by Kristoffer Bohmann

password_hash()is availble in PHP 5.5 and newer. I am surprised to learn it is not mentioned here.

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

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.

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

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

相反,使用 password_verify() 将用户提交的密码与存储在数据库中的唯一密码哈希进行比较。只需将用户名和密码哈希存储在用户数据库表中,然后您就可以使用 password_verify() 将其与用户提交的密码进行比较。

How password hash()'ing works:

密码 hash() 的工作原理:

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_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)
y$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq  <- This hash changes.
y.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
ym8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y 

To verify a hashed password, you use password_verify():

要验证散列密码,请使用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: y$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu  <- This password hash not change.

回答by ircmaxell

Replace rand(0,61)with mt_rand(0, 61)and you should be fine (Since mt_randis better at producing random numbers)...

替换rand(0,61)mt_rand(0, 61),你应该没问题(因为mt_rand更擅长产生随机数)......

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:

但比盐的强度更重要的是你散列它的方式。如果你有一个很好的盐常规,但只是这样做md5($pass.$salt),你就扔掉了盐。我个人建议拉伸散列...例如:

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

有关哈希拉伸的更多信息,请查看此 SO 答案...

回答by Drew Stephens

I would take advice from another answer and use mt_rand(0, 61), because the Mersenne Twister produces better entropy.

我会从另一个答案中获得建议并使用mt_rand(0, 61),因为 Mersenne Twister 会产生更好的熵。

Additionally, your function is really two parts: generating random $nbLettersdigits 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:

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

回答by Drew Stephens

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)

大气噪声由 random.org 提供。我还看到从通过色调和位置解释的熔岩灯图像中真正随机生成。(色调是位置)

回答by tor

Here is a much better way if you have windows and cant do /dev/random.

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

回答by Jay

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.

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

回答by William Wallace

I use this:

我用这个:

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

回答by NikiC

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

例如,我认为一个很好的盐是用户名(如果您谈论的是 pw 散列并且用户名不会改变。)

You don't need to generate anything and don't need to store further data.

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