开发者

Kohana: Understanding and reproducing Salt & Hashed passwords using the Auth Module

I'm using the Auth Module in Kohana v 2.3.4.

In terms of authenticating users, there's a two step process. The entry point is the function login. It's first task is to retrieve the password stored in the database and retrieve the password and determine the salt value. The salt is supposedly determined by an array of values, each corresponding to a point in the $salt.$password hashed value to introduce yet another part of the salt. In my case, I'm using md5.

Problems:

  1. I can't find a configuration for this SALT value. It seems to be relying on one already present within the password stored in the database. Is there one or do I need to configure AUTH to do so since this login needs to be portable and reproducible? If it can't detect the salt, in the hash_password routine, it defaults to using uniqid(), which I don't believe is portable at all.

  2. In terms of adding users, does it make sense to modify the Auth library to add this feature? ie, introduce my own customized SALT that I can say, do an MD5 hash on that and then use that md5 generated by the salt to seed the password at given points in the md5sum?

  3. I'm no security expert, but is this overkill? Granted, it prevents someone who were to get access to the md5 password list from using a md5 lookup of predetermined hashes.

  4. If you have used the Kohana PHP framework, if you have any lessons learned or experiences after using it that might give insight as to the right approach for this problem, let me know. I'm reading numerous forums and wiki's about it, and there isn't a real concrete opinion yet that I've seen. I'm essentially trying to get a reproducible approach for authenticating someone in this site, both using PHP and eventually from a mobile device, like an iPhone. I'm also thinking of eventually adding support for google friend connect for openID support and integration.

Below are snippets from the Auth module in Kohana concerning the functions of interest. They have some debugging in them as I'm trying to better understand what's going on.

public function login($username, $password, $remember = FALSE)
{
    if (empty($password))
        return FALSE;

    if (is_string($password))
    {
        // Get the salt from the stored password
        $salt = $this->find_salt($this->driver->password($username));
        Kohana::log('debug', "--- Auth_Core login salt = $salt ");
        Kohana::log('debug', "--- Auth_Core login pass = $password ");

        // Create a hashed password using the salt from the stored password
        $password = $this->hash_password($password, $salt);
    }
    Kohana::log('debug', "--- Auth_Core login pass_hash = $password ");
    return $this->driver->login($username, $password, $remember);
}
public function find_salt($password)
{
    $salt = '';

    foreach ($this->config['salt_pattern'] as $i => $offset)
    {
        // Find salt characters, take a good long look...
        //$salt .= $password[$offset + $i];
        $salt .= substr($password, $offset + $i, 0);
    }

    return $salt;
}
public开发者_开发百科 function hash_password($password, $salt = FALSE)
{
    Kohana::log('debug', "--- Auth_Core Original Pass = $password ");
    if ($salt === FALSE)
    {
        // Create a salt seed, same length as the number of offsets in the pattern
        $salt = substr($this->hash(uniqid(NULL, TRUE)), 0, count($this->config['salt_pattern']));
        Kohana::log('debug', "--- Auth_Core salt created = $salt ");
    }

    // Password hash that the salt will be inserted into
    $hash = $this->hash($salt.$password);

    // Change salt to an array
    $salt = str_split($salt, 1);

    // Returned password
    $password = '';

    // Used to calculate the length of splits
    $last_offset = 0;

    foreach ($this->config['salt_pattern'] as $offset)
    {
        // Split a new part of the hash off
        $part = substr($hash, 0, $offset - $last_offset);

        // Cut the current part out of the hash
        $hash = substr($hash, $offset - $last_offset);

        // Add the part to the password, appending the salt character
        $password .= $part.array_shift($salt);

        // Set the last offset to the current offset
        $last_offset = $offset;
    }

    Kohana::log('debug', "--- Auth_Core hashpw = $password + $hash ");

    // Return the password, with the remaining hash appended
    return $password.$hash;
}


Problem 1. The salt configuration is stored in config/auth.php. Find that file in modules/auth/config, then in your app/config folder (as you might have already known, Kohana uses cascading file system mechanism). The default file, which you are encouraged to customize into app/config/ folder, looks like below:

<?php defined('SYSPATH') OR die('No direct access allowed.');

return array
(
  'driver' => 'ORM',
  'hash_method' => 'sha1',
  'salt_pattern' => '1, 3, 5, 9, 14, 15, 20, 21, 28, 30',
  'lifetime' => 1209600,
  'session_key' => 'auth_user',
  'users' => array
  (
      // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
  ),
);

Problem 2. In my opinion, the password hashing mechanism used by Auth, which is SHA1 with salt insertion, is quite secure provided you keep your salts, i.e. your auth.php file, secure.

Problem 3. Auth built-in hashing mechanism uses SHA1, which is relatively more crack-proof than MD5, so I would say don't do the MD5 way, no matter how complicated your scheme might look. A security expert Thomas Ptacek in his blog wrote:

No, really. Use someone else’s password system. Don’t build your own.

Most of the industry’s worst security problems (like the famously bad LANMAN hash) happened because smart developers approached security code the same way they did the rest of their code.

Problem 4. Yup I'm using Kohana to build my small company website and some of our clients' website and so far I don't find any problem with the Auth module, although I can't say much since I haven't really used it for real security-concerned website. But in general, I'd say Kohana is an excellent framework especially with the cascading filesystem mechanism.


Regarding point 1, the hash_password() function is used both to generate the password hash (against the salt and including the salt) that is stored in the database (e.g. at signup-time), as well as to recreate that hash when the password needs to be verified (e.g. at login-time). The hash_password() function will encode any salt that is given (or uniqid() if none is given) in the password-hash itself; that's a form of encryption where the salt_pattern is the key; if the salt_pattern can be kept secret, then that provides additional security since an adversary will not be able to do offline brute-forcing of the hash since the method of hashing is not reproducible (if the salt_pattern can be kept secret):

// Signup time; forget about uniqid(); you can use any salt that
// you please; once the password hash is stored in the database there
// is no need to know where your salt came from since it will be
// included in the password hash.
$password_hash = hash_password($password, FALSE);

// Login time; note that the salt is taken from the password hash itself.
$reproduced = hash_password($password, find_salt($password_hash));
$verifies   = $password_hash == $reproduced;

The hash_password() function will first hash the password against the salt, and then insert each char of the salt into the password hash at the corresponding salt_pattern offset. find_salt() will extract these salt chars so that the hash can be reproduced. You can see it as hash_password() encrypting the salt and find_salt() decrypting it. Although you can also see it has hash_password() hiding the salt and find_salt() finding it, this method of encryption can't be called steganography, I think, because it is clear from the code that there is a salt stored with the password hash (the existence of the salt is not secret).

Regarding point 2, using your own salt is straightforward and fully compatible with the Auth module and an already existing database.

Regarding point 3, using a per user salt (uniqid() by default) is not overkill. Especially with MD5 which is broken for security purposes and where finding collisions is already practical with today's technology. Even better would be to use bcrypt() which uses a purposefully slower hashing algorithm to thwart brute-forcing attempts.

Regarding point 4, I haven't used the Kohana framework before, but reproducing or porting the Auth module is straightforward. Care must be taken that the salt_pattern is not forgotten or lost since it is an essential part of the hashing algorithm. The salt_pattern should also be kept secret since it is the only thing that keeps a determined adversary from brute-forcing the password hashes. uniqid() is just a reasonable default and can be replaced with whatever you want (as long as it is per-user and not a constant site-wide value.)


Also, there is a very good answer here on stackoverflow regarding portable bcrypt() and PHP. Naturally that will not be compatible with the Auth module, but I'd like to mention it anyway since it's just best practice to use a slow hash and not to rely on secrets that are difficult to keep, like the salt_patten.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜