Ignore:
Timestamp:
Nov 16, 2014 11:07:01 AM (10 years ago)
Author:
anonymous
Message:

Optimizing auth and csrf token.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/Auth_SQL.inc.php

    r500 r501  
    3232class Auth_SQL {
    3333
    34     // Available encryption types for class Auth_SQL.
     34    // Available hash types for class Auth_SQL.
    3535    const ENCRYPT_PLAINTEXT = 1;
    3636    const ENCRYPT_CRYPT = 2;
     
    6868        'db_login_table' => 'user_login_tbl',
    6969
    70         // The type of encryption to use for passwords stored in the db_table. Use one of the Auth_SQL::ENCRYPT_* types specified above.
    71         // Hardened password hashes rely on the same key/salt being used to compare encryptions.
     70        // The type of hash to use for passwords stored in the db_table. Use one of the Auth_SQL::ENCRYPT_* types specified above.
     71        // Hardened password hashes rely on the same key/salt being used to compare hashs.
    7272        // Be aware that when using one of the hardened types the App signing_key or $more_salt below cannot change!
    73         'encryption_type' => self::ENCRYPT_MD5,
     73        'hash_type' => self::ENCRYPT_MD5,
     74        'encryption_type' => null, // Backwards misnomer compatibility.
     75
     76        // Automatically update stored user hashes when the user next authenticates if the hash type changes (requires user_tbl with populated userpass_hashtype column).
     77        'hash_type_autoupdate' => true,
    7478
    7579        // The URL to the login script.
     
    257261    public function setParam($params)
    258262    {
     263        $app =& App::getInstance();
     264
    259265        if (isset($params['match_remote_ip_exempt_usernames'])) {
    260266            $params['match_remote_ip_exempt_usernames'] = array_map('strtolower', $params['match_remote_ip_exempt_usernames']);
     
    263269            $params['login_abuse_exempt_usernames'] = array_map('strtolower', $params['login_abuse_exempt_usernames']);
    264270        }
    265         if (isset($params['encryption_type']) && version_compare(PHP_VERSION, '5.5.0', '<') && in_array($params['encryption_type'], array(self::ENCRYPT_PASSWORD_BCRYPT, self::ENCRYPT_PASSWORD_DEFAULT))) {
     271        if (isset($params['encryption_type'])) {
     272            // Backwards misnomer compatibility.
     273            $params['hash_type'] = $params['encryption_type'];
     274        }
     275        if (isset($params['hash_type']) && version_compare(PHP_VERSION, '5.5.0', '<') && in_array($params['hash_type'], array(self::ENCRYPT_PASSWORD_BCRYPT, self::ENCRYPT_PASSWORD_DEFAULT))) {
    266276            // These hash types require the password_* userland lib in PHP < 5.5.0
    267277            $pw_compat_lib = 'vendor/ircmaxell/password-compat/lib/password.php';
     
    269279                include_once $pw_compat_lib;
    270280            } else {
    271                 $app =& App::getInstance();
    272                 $app->logMsg(sprintf('Encryption type %s requires password-compat lib in PHP < 5.5.0; falling back to ENCRYPT_SHA1', $params['encryption_type']), LOG_ERR, __FILE__, __LINE__);
    273                 $params['encryption_type'] = self::ENCRYPT_SHA1;
    274             }
     281                $app->logMsg(sprintf('Hash type %s requires password-compat lib in PHP < 5.5.0; falling back to ENCRYPT_SHA1_HARDENED', $params['hash_type']), LOG_ERR, __FILE__, __LINE__);
     282                $params['hash_type'] = self::ENCRYPT_SHA1_HARDENED;
     283            }
     284        }
     285        if (isset($params['hash_type']) && !in_array($params['hash_type'], array(self::ENCRYPT_PLAINTEXT, self::ENCRYPT_CRYPT, self::ENCRYPT_SHA1, self::ENCRYPT_SHA1_HARDENED, self::ENCRYPT_MD5, self::ENCRYPT_MD5_HARDENED, self::ENCRYPT_PASSWORD_BCRYPT, self::ENCRYPT_PASSWORD_DEFAULT))) {
     286            $app->logMsg(sprintf('Invalid hash type %s; falling back to ENCRYPT_SHA1_HARDENED', $params['hash_type']), LOG_ERR, __FILE__, __LINE__);
     287            $params['hash_type'] = self::ENCRYPT_SHA1_HARDENED;
    275288        }
    276289        if (isset($params) && is_array($params)) {
     
    393406        }
    394407
    395         if ($this->verifyPassword($password, $user_data['userpass'])) {
     408        $old_hash_type = isset($user_data['userpass_hashtype']) && !empty($user_data['userpass_hashtype']) ? $user_data['userpass_hashtype'] : $this->getParam('hash_type');
     409        if ($this->verifyPassword($password, $user_data['userpass'], $old_hash_type)) {
    396410            $app->logMsg(sprintf('Authentication successful for %s (user_id=%s)', $username, $user_data['user_id']), LOG_INFO, __FILE__, __LINE__);
    397411            unset($user_data['userpass']); // Avoid revealing the encrypted password in the $user_data.
     412            if ($this->getParam('hash_type_autoupdate') && $old_hash_type != $this->getParam('hash_type')) {
     413                // Let's update user's password hash to new type (just run setPassword with this authenticated password
).
     414                $this->setPassword($user_data['user_id'], $password);
     415                $app->logMsg(sprintf('User %s password hash type updated from %s to %s', $username, $old_hash_type, $this->getParam('hash_type')), LOG_INFO, __FILE__, __LINE__);
     416            }
    398417            return $user_data;
    399418        }
     
    835854     *
    836855     */
    837     public function encryptPassword($password, $salt=null)
     856    public function encryptPassword($password, $salt=null, $hash_type=null)
    838857    {
    839858        $app =& App::getInstance();
     
    841860        $password = (string)$password;
    842861
    843         // Existing password hashes rely on the same key/salt being used to compare encryptions.
     862        // Existing password hashes rely on the same key/salt being used to compare hashs.
    844863        // Don't change this (or the value applied to signing_key) unless you know existing hashes or signatures will not be affected!
    845864        $more_salt = 'B36D18E5-3FE4-4D58-8150-F26642852B81';
    846865
    847         switch ($this->_params['encryption_type']) {
     866        $hash_type = isset($hash_type) && !empty($hash_type) ? $hash_type : $this->getParam('hash_type');
     867
     868        switch ($hash_type) {
    848869        case self::ENCRYPT_PLAINTEXT :
    849870            $encrypted_password = $password;
     
    886907
    887908        default :
    888             $app->logMsg(sprintf('Authentication encrypt type specified is unrecognized: %s', $this->_params['encryption_type']), LOG_NOTICE, __FILE__, __LINE__);
     909            $app->logMsg(sprintf('Unknown hash type: %s', $hash_type), LOG_WARNING, __FILE__, __LINE__);
    889910            return false;
    890             break;
    891911        }
    892912
    893913        // In case our hashing function returns 'false' or another empty value, bail out.
    894914        if ('' == trim((string)$encrypted_password)) {
    895             $app->logMsg(sprintf('Invalid password hash returned; check yo crypto!', null), LOG_ALERT, __FILE__, __LINE__);
     915            $app->logMsg(sprintf('Invalid password hash returned ("%s") for hash type %s; check yo crypto!', $encrypted_password, $hash_type), LOG_ALERT, __FILE__, __LINE__);
    896916            return false;
    897917        }
     
    910930    * @since    15 Nov 2014 21:37:28
    911931    */
    912     public function verifyPassword($password, $encrypted_password)
    913     {
    914         switch ($this->_params['encryption_type']) {
     932    public function verifyPassword($password, $encrypted_password, $hash_type=null)
     933    {
     934        $app =& App::getInstance();
     935
     936        $hash_type = isset($hash_type) && !empty($hash_type) ? $hash_type : $this->getParam('hash_type');
     937
     938        switch ($hash_type) {
    915939        case self::ENCRYPT_CRYPT :
    916940            return $this->encryptPassword($password, $encrypted_password) == $encrypted_password;
     
    928952            return password_verify($password, $encrypted_password);
    929953        }
    930     }
    931 
    932     /**
    933      *
    934      */
    935     public function setPassword($user_id=null, $password)
     954
     955        $app->logMsg(sprintf('Unknown hash type: %s', $hash_type), LOG_WARNING, __FILE__, __LINE__);
     956        return false;
     957    }
     958
     959    /**
     960     *
     961     */
     962    public function setPassword($user_id=null, $password, $hash_type=null)
    936963    {
    937964        $app =& App::getInstance();
     
    943970        $user_id = isset($user_id) ? $user_id : $this->get('user_id');
    944971
    945         // Get old password.
    946         $qid = $db->query("
    947             SELECT userpass
    948             FROM " . $this->_params['db_table'] . "
    949             WHERE " . $this->_params['db_primary_key'] . " = '" . $db->escapeString($user_id) . "'
    950         ");
    951         if (!list($old_encrypted_password) = mysql_fetch_row($qid)) {
    952             $app->logMsg(sprintf('Cannot set password for nonexistent user_id %s', $user_id), LOG_WARNING, __FILE__, __LINE__);
    953             return false;
    954         }
    955 
    956         // Compare old with new to ensure we're actually *changing* the password.
    957         if ($this->verifyPassword($password, $old_encrypted_password)) {
    958             $app->logMsg(sprintf('Not setting password: new is the same as old.', null), LOG_INFO, __FILE__, __LINE__);
    959             return null;
    960         }
     972        // New hash type.
     973        $hash_type = isset($hash_type) ? $hash_type : $this->getParam('hash_type');
    961974
    962975        // Save the hash method used if a table exists for it.
    963         $userpass_hashtype = '';
     976        $userpass_hashtype_clause = '';
    964977        if ($db->columnExists($this->_params['db_table'], 'userpass_hashtype', false)) {
    965             $userpass_hashtype = ", userpass_hashtype = '" . $db->escapeString($this->getParam('encryption_type')) . "'";
     978            $userpass_hashtype_clause = ", userpass_hashtype = '" . $db->escapeString($hash_type) . "'";
    966979        }
    967980
     
    969982        $db->query("
    970983            UPDATE " . $this->_params['db_table'] . "
    971             SET userpass = '" . $db->escapeString($this->encryptPassword($password)) . "'
    972             $userpass_hashtype
     984            SET userpass = '" . $db->escapeString($this->encryptPassword($password, null, $hash_type)) . "'
     985            $userpass_hashtype_clause
    973986            WHERE " . $this->_params['db_primary_key'] . " = '" . $db->escapeString($user_id) . "'
    974987        ");
    975988
    976989        if (mysql_affected_rows($db->getDBH()) != 1) {
    977             $app->logMsg(sprintf('Failed to update password for user_id %s', $user_id), LOG_WARNING, __FILE__, __LINE__);
     990            $app->logMsg(sprintf('Failed to update password for user_id %s (no affected rows)', $user_id), LOG_WARNING, __FILE__, __LINE__);
    978991            return false;
    979992        }
Note: See TracChangeset for help on using the changeset viewer.