Very Secure Password Hashing using unknown salts.

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Very Secure Password Hashing using unknown salts.

Post by Benjamin »

This code creates a variable length salt to the left and right of a fixed length password hash using the password length to determine the characters allocated for the salt. This makes it impossible to determine what part of the hash is the the salt vs what part of the hash is the actual hash.

Just posting this in case anyone finds it useful. Modify to suit.

Code: Select all

<?php
/**
 * Class asPasswordHash
 *
 * A very secure password hashing scheme using the whirlpool algorythm
 * and creating an impossible to find salt without knowing the actual
 * length of the password.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @package
 * @author     Benjamin Lewis
 * @version    1.0
 */
class asPasswordHash
{
    /**
     * Contains the algorythm used for hashing passwords
     *
     * @var string
     * @access private
     */
    private $_algorythm = 'whirlpool';

    /**
     *
     * @param string $algo The hashing algorythm to use
     * @access public
     */
    public function __construct($algo = null) {
        $this->_algorythm = $algo === null ? $this->_algorythm : $algo;
    }

    /**
     * Sets the algorythm to use when creating password hashes
     *
     * @var string The algorythm to use
     * @return object An instance of this class
     * @access public
     */
    public function setAlgo($algo) {
        $this->_algorythm = $algo;
        return $this;
    }

    /**
     * Gets the algorythm currently being used to create hashes
     *
     * @return string The current alogrythm in use
     */
    public function getAlgo() {
        return $this->_algorythm;
    }

    /**
     * Creates a salted password hash with the salt embedded in the actual
     * hash.
     *
     * @param  string $password The actual password
     * @param  string $hash The hash to check a password against
     * @return string The hashed password
     */
    public function get($password, $hash = null) {
        if (!in_array($this->_algorythm, hash_algos())) {
            trigger_error(sprintf("%s is not an available hashing algorythm", $this->_algorythm), E_USER_ERROR);
        }

        /*
         * If a hash is not specified, one is created by hashing a random
         * 256 byte string.
         */
        if ($hash === null) {
            $salt = '';
            for ($i = 0; $i <= 256; $i++) {
                $salt .= chr(mt_rand(0, 255));
            }

            $hashedSalt = hash($this->_algorythm, $salt);
        } else {
            $hashedSalt = $hash;
        }

        /*
         * The length of the salt to use is set by dividing the length
         * of the generated hash for this algorythm by 6 and rounding
         * down.
         */
        $saltLen = floor(strlen($hashedSalt) / 6);

        /*
         * Save the length of the password for later use.
         */
        $passwdLen = strlen($password);

        /*
         * While the length of the password is greater than the salt length,
         * multiply the length of the password by the value of the last digit
         * of the length divided by 10.
         *
         * Example:
         *     Password Length = 1009
         *     (9 / 10) = 0.9
         *     (1009 * 0.9) = 908.1
         *     New Password Length = 908.1
         */
        while ($passwdLen > $saltLen) {
            $passwdLen = $passwdLen * (substr($passwdLen, -1) / 10);
        }

        /*
         * We need $passwdLen to be an absolute integer value
         */
        $passwdLen  = (integer) abs($passwdLen);

        /*
         * Subtracting the length of the password from the length of the
         * salt that we will be using gives us the number of salt characters
         * to use in the final hash
         */
        $saltLen -= $passwdLen;

        /*
         * Populate the left and right salt values from the hashed salt using
         * the values we calculated earlier
         */
        $leftSalt  = substr($hashedSalt, 0, $passwdLen);
        $rightSalt = substr($hashedSalt, -$saltLen, $saltLen);

        /*
         * Generate the password hash
         */
        $passwdHash = hash($this->_algorythm, ($leftSalt . $password . $rightSalt));

        /*
         * Cut off left and right portions of the hashed password.  These will
         * be replaced by the left and right salts.
         */
        if ($saltLen == 0) {
            $passwdHash = substr($passwdHash, $passwdLen);
        } else {
            $passwdHash = substr($passwdHash, $passwdLen, -$saltLen);
        }

        /*
         * Return the left salt, password hash and right salt
         */
        return $leftSalt . $passwdHash . $rightSalt;
    }
}
Usage:

Code: Select all

    public static function isUserPassword($password, $passwordHash) {
        $pHash = new asPasswordHash();
        return $passwordHash == $pHash->get($password, $passwordHash);
    }

    public function setPassword($password) {
        /*
         * Every time this method is called a new password hash will be
         * generated, even if the password has not been changed.  This
         * check will stop the password update if it has not been changed
         * by comparing the password hash with the password posted.
         */
        $pHash = new asPasswordHash();
        if ($this->password == $pHash->get($password, $this->password)) {
            return $this;
        } else {
            return parent::setPassword($pHash->get($password));
        }
    }

Code: Select all

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(25) NOT NULL,
  `email` varchar(64) NOT NULL,
  `password` varchar(128) NOT NULL,
  `reg_ip_lan` int(11) DEFAULT NULL,
  `reg_ip_wan` int(11) DEFAULT NULL,
  `last_login` datetime DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_U_1` (`user_name`),
  UNIQUE KEY `user_U_2` (`email`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

Looks good, a clever way to eliminate the extra table column.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

Well, in reality a salt is useless if it is known.
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

What do you mean? Isn't it unknown stored in a database table?
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

Sure. But so is the hash. So why use the salt in the first place;)
Peter Kelly
Forum Contributor
Posts: 143
Joined: Fri Jan 14, 2011 5:33 pm
Location: England
Contact:

Re: Very Secure Password Hashing using unknown salts.

Post by Peter Kelly »

Its very good but surely doing

Code: Select all

$password = md5($password . $password . $password);
Is just as secure as it basically has a salt as you cant work out the salt or the password without knowing the password.
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

Wait, what hash?
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Very Secure Password Hashing using unknown salts.

Post by VladSun »

Benjamin wrote:Well, in reality a salt is useless if it is known.
Benjamin wrote:Sure. But so is the hash. So why use the salt in the first place;)
I think rainbow attacks or dictionary attacks are useless (or at least very hard to apply) for salted hashes even if the salt is known. Yes, a "secret" salt will increase the security, but if the salt is long enough it really doesn't matter.
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

VladSun wrote:I think rainbow attacks or dictionary attacks are useless (or at least very hard to apply) for salted hashes even if the salt is known.
What makes you say this?
Jonah Bron wrote:Wait, what hash?
The one stored in the database.
Peter Kelly wrote:Its very good but surely doing

Code: Select all

$password = md5($password . $password . $password);
Is just as secure as it basically has a salt as you cant work out the salt or the password without knowing the password.
No. An attacker only needs to know how the hashes are generated in order to break them.
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

Benjamin wrote:The one stored in the database.
You mean the hash of the password?
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

Yeah
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

I see. So if they have the database and the code, the salt won't help?
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

This wikipedia entry covers the topic pretty well: http://en.wikipedia.org/wiki/Salt_%28cryptography%29
User avatar
Jonah Bron
DevNet Master
Posts: 2764
Joined: Thu Mar 15, 2007 6:28 pm
Location: Redding, California

Re: Very Secure Password Hashing using unknown salts.

Post by Jonah Bron »

Okay, I read it. Does one store a hash of the salt or the salt itself? I'm still not entirely clear on how it secures things if a malicious person has access to the database.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Very Secure Password Hashing using unknown salts.

Post by Benjamin »

Well most of the code I've seen that actually *does* use a salt generally prefixes the password with it, so it's easy to find.

My class above creates a random, variable length salt that is mixed in with the actual password hash, so it's extremely difficult to determine what the salt actually is.
Post Reply