且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在我的登录名中加入 salt 以获取密码?

更新时间:2023-01-02 10:55:04

哈希函数总是为相同的输入字符串返回相同的值.假设我的用户 (Alice) 拥有密码 secret.散列 secret 使用 md5() 导致以下散列

An hash function always return the same value for the same input string. Let's say my user (Alice) has the password secret. Hashing secret using md5() leads to the following hash

5ebe2294ecd0e0f08eab7690d2a6ee69

使用字典(常用词和密码列表)或为您提供该服务的各种站点之一,攻击者 (Mallory) 在他的字典中看到 时,可以轻松找出密码是秘密的5ebe2294ecd0e0f08eab7690d2a6ee69 = 秘密.

Using a dictionary (a list of common words and password) or one of the various sites that offer you that service, the attacker (Mallory) can easily find out the password is secret when he sees in his dictionary that 5ebe2294ecd0e0f08eab7690d2a6ee69 = secret.

在散列之前加盐的过程使得在不知道你的盐的情况下更难使用字典攻击.考虑以下几点:

The process of salting before hashing makes it harder to use a dictionary attack without knowing your salt. Consider the following:

<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');

得到的哈希值现在是 b58ad809eece17322de5024d79299f8a 但 Alice 的密码仍然是 secret.现在,如果 Mallory 得到了咸味哈希,她很可能不会在她的字典中找到答案.如果她这样做,字典会给她错误的答案.

The resulting hash is now b58ad809eece17322de5024d79299f8a but Alice's password is still secret. Now if Mallory gets her hands on the salted hash, chances are she will not find the answer in her dictionary. If she does, the dictionary will give her the wrong answer.

永远不要在数据库中存储静态盐.***将它与您的应用程序的配置一起存储(顺便说一下,该配置不应从网络上获得).

Never store a static salt in your database. Preferably store it with your application's configuration (which by the way should not be available from the web).

如果您打算使用动态盐,您将需要使用数据库.使用现有有效数据的非空列来构建您的 salt(基于秘密加密密钥的 blowfish 加密的用户名字符串通常是加密安全的).不要为盐使用单独的列.如果您不能使用现有列,请将您的盐与您的哈希合并在同一列中.例如,对 128 位盐使用前 32 个字符,然后对 160 位散列使用最后 40 个字符.以下函数将生成这样的哈希:

If you are going to use a dynamic salt, you are going to need to use the database. Use a non-null column of existing valid data to build your salt on (blowfish-encrypted string of username based on a secret encryption key is usually cryptographically secure). Do not use a separate column for the salt. If you cannot use an existing column, incorporate your salt in the same column than your hash. For example, use the first 32 characters for your 128-bits salt and then the last 40 for your 160-bits hash. The following function will generate such an hash:

function seeded_sha1($string, $seed_bits) {
    if(($seed_bits % 8) != 0) {
        throw new Exception('bits must be divisible by 8');
    }

    $salt = '';
    for($i = 0; $i < $seed_bits; $i+=8) {
        $salt .= pack('c', mt_rand());
    }

    $hexsalt = unpack('h*hex', $salt);

    return $hexsalt['hex'] . sha1($salt . $string);
}

function compare_seeded_sha1($plain, $hash) {
    $sha1 = substr($hash, -40);
    $salt = pack('h*', substr($hash, 0, -40));

    $plain_hash = sha1($salt . $plain);
    return ($plain_hash == $sha1);
}

如果攻击者使用 SQL 注入进入您的数据库,那么至少他/她检索的哈希将没有用,因为他/她将无法访问您的应用程序配置.如果您的服务器获得 root 权限,那么无论您做什么几乎都结束了.

If an attacker gets in your database using SQL injection, at least the hashes he/she retrieves won't be useful since he/she won't have access to your application configuration. If your server gets rooted, it's pretty much game-over no matter what you do.

注意:md5()上还可能存在其他类型的攻击> 这就是为什么你使用更安全的散列算法,sha1()例如.或者,更好的是使用 便携式 PHP 密码散列框架,它的设计考虑了安全性和向后兼容几乎所有 PHP 版本.

Note: There are other types of attack possible on md5() which is why you use more secure hashing algorithm, sha1() for example. Or, even better, use the Portable PHP password hashing framework, which has been designed with security in mind and is backwards compatible with pretty much any PHP version.

require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}