Crypt()盐值生成和密码加密,执行良好?

4

以下是我用于密码加密和验证的一些函数。想知道这是否是一个好的处理方式。我正在使用CodeIgniter框架。

这是“加密”函数:

function crypt_pass( $input ){
    $salt = substr(sha1(date('r')), rand(0, 17), 22);
    $cost = 10;
    $hash = '$2y$' . $cost . '$' . $salt;

    $pw_and_salt['pw'] = crypt($input, "$hash");
    $pw_and_salt['salt'] = $salt;

    return $pw_and_salt;
}

我在数据库中存储密码和盐值。以下是登录函数:

function login(){

    $this->db->select('salt');
    $salt = $this->db->get_where('users', array('username' => $this->input->post('username') ) )->row();



    $where = array(
        'username' => $this->input->post('username'),
        'password' => crypt( $this->input->post('password'), '$2y$10$' . $salt->salt),
    );


    $user = $this->db->get_where('users', $where)->first_row();

    if (!$user) {
        return FALSE;
    }else{
        if(!empty($user->activation)){

            return 2;

        }else if($user && empty($user->activation)){
            $this->session->set_userdata('id',$user->id);
            $this->session->set_userdata('username',$user->username);
            $this->session->set_userdata('first_name',$user->first_name);   

            return 1;
        }
    }
}

我是否以正确的方式实现了这个功能?这样做是否足够安全?

版本2:不存储盐,在数据库中从密码中提取:

function login(){

    $this->db->select('password');

    $pw = $this->db->get_where('users', array('username' => $this->input->post('username') ) )->row();


    $where = array(
        'username' => $this->input->post('username'),
        'password' => crypt( $this->input->post('password'), $pw->password),
    );

    $user = $this->db->get_where('users', $where)->first_row();

    if (!$user) {

        return FALSE;

    }else{

        if(!empty($user->activation)){

            return 2;

        }else if($user && empty($user->activation)){

            $this->session->set_userdata('id',$user->id);
            $this->session->set_userdata('username',$user->username);
            $this->session->set_userdata('first_name',$user->first_name);   

            return 1;
        }
    }
}

1
我认为这个问题最好在 代码审查 栈上进行。 - Taylan Aydinli
另外,还可以查看Openwall的PHP密码哈希框架(PHPass)。它是可移植的,并且针对用户密码的许多常见攻击进行了加固。编写该框架的人(SolarDesigner)也是编写John The Ripper并担任密码哈希竞赛的评委的人。因此,他对密码攻击有一定的了解。 - jww
这个问题似乎适合于 Stack Exchange 网络中的另一个站点。也许你应该尝试 Code Review Stack Exchange - jww
$pw_and_salt['pw'] = crypt($input, "$hash"); 为什么 $hash 要用引号括起来?这样不应该吧? - Boris Gafurov
1个回答

6

虽然存在一些需要改进的地方,但首先我建议使用PHP的新功能password_hash()。此函数将生成安全盐并将其包含在结果哈希值中,因此您可以将其存储在单个数据库字段中。还有一个兼容包用于早期版本。

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

关于你的代码,有一些想法:

  1. 你使用crypt()生成BCrypt哈希值,所以盐值将成为结果哈希值的一部分。没有必要单独存储它。
  2. 盐值的生成可以得到改进,使用操作系统MCRYPT_DEV_URANDOM的随机源。
  3. 如果你将成本因素更改为9,则格式将变得无效,因为crypt期望两个数字。

嗨,马丁,感谢您的建议。我一定会使用您的建议。关于当前设置的一个问题,如果我没有弄错,我只能通过以与我在数据库中存储密码时相同的方式对其进行哈希处理来验证输入的密码,那么我如何在不实际存储和检索盐的情况下完成这个过程呢? - Jursels
我将我的验证方法添加到了原帖,并且没有从数据库中获取盐值。 - Jursels
@Jursels - 盐已经包含在生成的哈希值中,而password_verify()函数将从中提取它,同时也会提取成本因素。例如:哈希值 $2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa 在第三个 $ 后面包含盐,盐为 nOUIs5kJ7naTuTFkBy1veu - martinstoeckli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接