在阅读了有关 salts 密码哈希的信息后,我想为我正在构建的站点的管理区域实现一个简单的版本。
如果您有任何很好的代码链接可以很好地实现这个想法,如果您能分享,我将不胜感激。
谢谢,
注册过程:用户输入密码。系统从随机数据中生成一个盐值(可以是时间和 PID 或其他东西的哈希值)。系统生成密码和盐值的哈希值,并将这两者存储在注册表中。
登录过程:用户输入密码。系统从数据库中提取盐值并将其与密码进行哈希处理,并将其与注册期间放入数据库的哈希密码值进行比较。
明文密码永远不会存储在数据库中。客户端永远不会看到盐值。
function encodePwd($salt, $string) {
return sha1( $salt . $string );
}
不过考虑一下盐随机化。专门的密码编码。
如果我有“随机”的盐和“复杂”的密码,我的 sha1 将是
e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
存储在数据库中。我想检查一下。
因此,当用户向我提供“复杂”密码时,我在其前面标记“随机”并对其进行编码以获得相同的哈希值。如果它们相等,那么 bazinga!我准备好了。
但如果那是随机的呢?
存储时的盐:“随机”
SHA1: e55ec45f2873a04d2b888a5f59dd3f9d3bb25329
用户放入时加盐:“apple”
SHA1: e07b207d77a0bd27d321552fc934b186559f9f42
我将如何匹配这些?
如果您正在寻找一种更安全的方法,请使用您拥有的数据,并且这些数据是不变的,例如用户的用户名或 id 或其他东西(最好是不会改变的东西)。你需要一个可以依赖的模式。
用户名会很好用(如果他们更改了用户名,您必须确保更新密码)这样身份验证可能看起来像
`WHERE `username` = '&username' AND `password` = '" . encodePwd( $username, $password ) . "'"`
function encodePwd( $username, $password) {
// maybe modify username on a non-random basis? - like
// $username = sha1( substr($username, 2)); // assuming usernames have a min-length requirement
return sha1( $username . $password ) ;
}
好吧,这就是我要做的:
function makeToken($length = 16) {
if ($length > 16) {
$ret = '';
while ($length > 0) {
$ret .= makeToken(16);
$length -= 16;
}
if ($length < 0) {
$ret = substr($ret, 0, $length);
}
return $ret;
}
$stub = '';
for ($i = 0; $i < 100; $i++) {
$stub .= chr(mt_rand(1, 254));
}
$hash = sha1($stub);
$hashLen = strlen($hash);
$ret = '';
for ($i = 0; $i < $length; $i++) {
$ret .= $hash[mt_rand(0, $hashLen - 1)];
}
return $ret;
}
function makeSaltedHash($string, $salt = '') {
if (empty($salt)) {
$salt = makeToken();
}
$hash = '';
for ($i = 0; $i < 200; $i++) {
$hash = sha1($hash . $salt . $string);
}
return $hash . ':' . $salt;
}
function verifySaltedHash($string, $hash) {
if (strpos($string, ':') === false) return false;
list ($base, $salt) = explode(':', $hash);
$test = makeSaltedHash($string, $salt);
return $test === $hash;
}
道理是这样的:
首先,生成一个随机盐(这将始终返回一个十六进制字符串,因此它可以用于令牌等)。然后循环一个哈希函数(在本例中为 sha1)超过 1 次。这使得生成彩虹表变得更加昂贵(在这种情况下是成本的 200 倍),而生成的费用相对较少。
用法:
要生成哈希:
$hash = makeSaltedHash($password);
要验证哈希:
$bool = verifySaltedHash($password, $savedHash);
要生成令牌(CSRF 保护、session_id 等),您可以通过以下几种方式进行:
固定长度:
$token = makeToken(32);
随机长度:
$token = makeToken(mt_rand(64,128));
编辑:在散列函数中增加了对 sha1 的重复。
我没有可用代码的链接,但我过去所做的是生成随机盐 -$salt = rand(1,1000000000);
并将其保存在会话中。我将该盐传递给登录页面,然后使用 JavaScript 创建一个盐 + 密码的 SHA 哈希,该哈希是提交的,而不是明文密码。由于盐存储在会话中,因此我可以使用它来查看登录哈希是否与存储在数据库中的盐 + 密码哈希匹配。
如果您需要真正安全的哈希,请使用Portable PHP hashing framework。
我还推荐本月的 PHP 安全文章,该文章广泛涉及密码散列和散列的安全性。
有很多方法可以创建盐串,但我认为你不需要过多考虑盐的强度。
我像这样散列密码
$hash = sha1(strlen($password) . md5($password) . $salt);
我认为它是速度和“安全性”之间的最佳表现。
function salt($lenght = 9) {
$numbers = '0123456789';
$chars = 'qwertzuiopasdfghjklyxcvbnm';
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {
if ($alt == 1)
{
$password .= $chars[(rand() % strlen($chars))];
$alt = 0;
} else
{
$password .= $numbers[(rand() % strlen($numbers))];
$alt = 1;
}
}
return $password;
}
代码很简单,dan heberden 已经提供了。
盐只是在生成哈希之前附加或附加到密码的一段文本。例如,如果您的密码是 'password' 而 salt 是 'salt',那么哈希值将hashFunction('saltpassword')
不是hashFunction('password')
.
盐通常用于避免彩虹密码破解 - 这是根据哈希密码检查大量密码及其哈希值的地方。例如,在上面的示例中,假设有一个对应于 的哈希 123456 hashFunction('password')
,如果攻击者知道您的哈希是 123456,那么他们知道您的密码是“密码”。
你的盐应该是一个随机的字母和数字串——例如kjah!!sdf986。某人拥有包含 kjah!!sdf986password 的彩虹表的可能性非常小,因此即使有人获得了您的散列密码,那也没什么用。
但是,您显然需要每次都使用相同的盐,或者至少存储盐和密码。因为如果您每次都选择随机盐,那么您的散列盐+密码可能会不一样:D