Secure Password Encryption using BCrypt with PHP

Implementing login functionality in your application comes with the responsibility of securely storing your users’ passwords in your database. Storing the password as plain text is highly irresponsible since a hacked database will directly expose your users’ passwords. Since people generally re-use the same login information across every product that they use, all of their accounts would become vulnerable!

Implementation

When a new visitor registers their account, the password can be securely hashed using the BCrypt algorithm. There are a number of articles explaining the differences between different hashing algorithms and a common consensus is that BCrypt is the only one that should be used for password hashing. You can find more information about password hashing and BCrypt from the following articles:

Call the hash method to hash a raw password.

$strHashedPassword = BCrypt::hash( $strPassword );

When verifying a login, use the compare function.

if( BCrypt::compare( $strRawPassword, $strHashedPassword ) ) {
	// Login!
} else {
	// Invalid.
}

In your database, the password column can be of type varchar with a length of 60. All of the hashed passwords will be of length 60.

Below is the standalone class implementation of BCrypt. If you are using PHP 5.5, bcrypt is directly implemented using password_hash.

BCrypt

<?php

    /**
     * @class
     * Provides secure and safe hashing for passwords.
     */
    class BCrypt {

        /**
         * Work factor cost boundaries and default.
         */
        const COST_MIN = 4;
        const COST_MAX = 31;
        const COST_DEFAULT = 12;
	
        /**
         * Generates a BCrypt salt.
         * @param strSalt
         * @return
         */
        private static function salt( $strSalt ) {
            if( $strSalt !== null ) {
                $strSalt = sha1( $strSalt );
            } else if( function_exists( "openssl_random_pseudo_bytes" ) ) {
                $strSalt = openssl_random_pseudo_bytes( 16 );
            } else {
                mt_srand();
                $strSalt = sha1( mt_rand() . uniqid() . time() );
            }
            
            return substr( strtr( base64_encode( $strSalt ), "+", "." ), 0, 22 );
        }
    
        /**
         * Hashes a password using BCrypt.
         * @param strPassword The password to hash.
         * @param intCost The cost parameter. Must be between 4 - 31. Default is 12.
         * @param strSalt A salt to use.
         * @return
         */
        public static function hash( $strPassword, $intCost = self::COST_DEFAULT, $strSalt = null ) {
            $intCost = intval( $intCost );

            // Make sure it's in the range.
            if( $intCost < self::COST_MIN ) {
                $intCost = self::COST_MIN;
            } elseif( $intCost > self::COST_MAX ) {
                $intCost = self::COST_MAX;
            }
     
            $strSalt = (string) self::salt( $strSalt );
            $intCost = (string) str_pad( $intCost, 2, "0", STR_PAD_LEFT );

            return crypt( $strPassword, '$2a$' . $intCost . '$' . $strSalt . '$' );
        }
    
        /**
         * Compares a password to a hash.
         * @param strPassword The password to compare.
         * @param strHash The password hash to compare.
         * @return
         */    
        public static function compare( $strPassword, $strHash ) {
            return ( $strHash === crypt( $strPassword, $strHash ) );
        }

    }

?>

This class was based based off of A Lightweight PHP BCrypt class.

Leave a Reply

Your email address will not be published. Required fields are marked *