#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    5
    Rep Power
    0

    How secure is the way I'm storing my passwords?


    Hey all, I'm looking for input on the way I'm storing my passwords. I'm rather new to PHP and the security concepts there-under and come from a heavily object oriented background. If you guys could let me know of any obvious security holes in this method of password storage, I'd love to hear them. Thanks a ton.

    The passwords are stored in SQL using a `blob` object type. They are created using the following code. Let me know any input you guys have on how I could improve this, or what might be insecure/wrong about the way I'm doing it. I'm open to any criticism as I am newer to PHP.

    Thanks again

    PHP Code:
        $passwordObject = new Password("mysecretpassword"); 

    The Password Class
    PHP Code:
    <?php
        
    /**
         * An instance of a Password strorage object
         */
        
    class Password {
            private 
    $salt;
            private 
    $password;
            
            private 
    $wasForced false;
            private 
    $containsSymbol false;
            private 
    $containsNumber false;
            private 
    $containsUppercase false;
            private 
    $RawLength 0
            
            
    /**
             * Construct the Password object
             * @param String $password the password to pass the object
             * @param number $length The length of the salt (Optional)
             * @param string $salt The Salt to use
             * @param boolean force password text
             */
            
    function __construct($password$randomsaltlength=SECURITY_SALT_LENGTH$salt null$force false){
                
                if(!
    $force){
                    
                    foreach(
    str_split(SECURITY_PASSWORD_REQUIRED_SYMBOLS1) as $symbol){
                        if(
    strpos($password$symbol) !== false){
                            
    $this->containsSymbol true;
                            break;
                        }
                    }
                        
                    if(
    preg_match('/[0-9]/'$password))
                        
    $this->containsNumber true;
                        
                    if(
    preg_match('/[A-Z]/'$password))
                        
    $this->containsUppercase true;
                    
                    
    $this->RawLength strlen($password);
                    
                    
    $this->salt $salt ?: "";
                    
                    if(
    $this->salt == ""$this->setRandomSalt(($randomsaltlength 4) ? $randomsaltlength 5);
                    
    $this->password Password::getHash($password$this->salt);
                }else{
                    
    $this->wasForced true;
                    
    $this->password $password;
                    
    $this->salt $salt;
                }
            }
        
            
    /**
             * Compares another password to the password in the object
             * @param string $password The password to compare
             * @return boolean true if passwords match, false if they don't
             */
            
    public function compareTo($password){
                return (
    Password::getHash($password$this->salt) == $this->password);
            }
        
            
    /**
             * Gets the encrypted version of the password, using md5, sha256 and the salt
             * @return string The encrypted password
             */
            
    public function getEncryptedPassword(){
                return 
    $this->password;
            }
        
            
    /**
             * Gets the salt used to encrypt the password
             * @return string The salt used to encrypt the password
             */
            
    public function getSalt(){
                return 
    $this->salt;
            }
            
            
    /**
             * Checks if currently defined password is valid with system security requirements.
             * (Returns true if password was forced) 
             * @return boolean
             */
            
    public function isValidInSystem(){
                if(
    $this->wasForced) return true;

                
    $ret true;
                
                if(
    SECURITY_PASSWORD_REQUIRE_UPPERCASE)
                    if (!
    $this->containsUppercase$ret false;

                if(
    SECURITY_PASSWORD_REQUIRE_SYMBOL)
                    if (!
    $this->containsSymbol$ret false;
                
                if(
    SECURITY_PASSWORD_REQUIRE_NUMBER)
                    if (!
    $this->containsNumber$ret false;
                
                if (
    $this->RawLength SECURITY_PASSWORD_MIN_LENGTH$ret false;;
                
                return 
    $ret;
            }
            
            
    /**
             * Gets the raw passwords length
             * @return Integer
             */
            
    public function getRawLength(){
                return 
    $this->RawLength;
            }
            
            
    /**
             * Checks if the password was forced
             * @return boolean
             */
            
    public function isForced(){
                return 
    $this->wasForced;
            }
            
            
    /**
             * Checks if the raw password contained an uppercase letter
             * @return boolean
             */
            
    public function hasUppercase(){
                return 
    $this->containsUppercase;
            }
            
            
    /**
             * Checks if the raw password contained a number
             * @return boolean
             */
            
    public function hasNumber(){
                return 
    $this->containsNumber;
            }
            
            
    /**
             * Checks if the raw password contained a symbol
             * @return boolean
             */
            
    public function hasSymbol(){
                return 
    $this->containsSymbol;
            }
            
            
    /**
             * Sets a random initial value for $salt
             * @param unknown $length the length of the initial string
             */
            
    private function setRandomSalt($length){
                while(
    $length--) $this->salt .= chr(mt_rand(97122)) ;
                
    $this->salt hash('sha256'$this->salt);
            }
            
            
    /**
             * Returns a password hash based on the data passed
             * @param String $password The password to use
             * @param String $salt The salt to use
             * @return String The password hash
             */
            
    public static function getHash($password$salt){
                return 
    Password::pbkdf2("sha256"$password$salt100050);
            }
            
            
            
    /**
             * Current pbkdf2 standard password hashing algorithm
             * @param unknown $algorithm
             * @param unknown $password
             * @param unknown $salt
             * @param unknown $count
             * @param unknown $key_length
             * @param string $raw_output
             * @return string
             */
            
    static function pbkdf2($algorithm$password$salt$count$key_length$raw_output false)
            {
                
    $algorithm strtolower($algorithm);
                if(!
    in_array($algorithmhash_algos(), true))
                    
    trigger_error('PBKDF2 ERROR: Invalid hash algorithm.'E_USER_ERROR);
                if(
    $count <= || $key_length <= 0)
                    
    trigger_error('PBKDF2 ERROR: Invalid parameters.'E_USER_ERROR);
            
                if (
    function_exists("hash_pbkdf2")) {
                    
    // The output length is in NIBBLES (4-bits) if $raw_output is false!
                    
    if (!$raw_output) {
                        
    $key_length $key_length 2;
                    }
                    return 
    hash_pbkdf2($algorithm$password$salt$count$key_length$raw_output);
                }
            
                
    $hash_length strlen(hash($algorithm""true));
                
    $block_count ceil($key_length $hash_length);
            
                
    $output "";
                for(
    $i 1$i <= $block_count$i++) {
                    
    $last $salt pack("N"$i);
                    
    $last $xorsum hash_hmac($algorithm$last$passwordtrue);
                    for (
    $j 1$j $count$j++) {
                        
    $xorsum ^= ($last hash_hmac($algorithm$last$passwordtrue));
                    }
                    
    $output .= $xorsum;
                }
            
                if(
    $raw_output)
                    return 
    substr($output0$key_length);
                else
                    return 
    bin2hex(substr($output0$key_length));
            }
        }
    ?>
  2. #2
  3. A Change of Season
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Mar 2004
    Location
    Next Door
    Posts
    3,066
    Rep Power
    175
    Why are you doing all that? Just use password hash...

    Comments on this post

    • Strider64 agrees
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    5
    Rep Power
    0
    The original reason for the object storage method that I'm using above was to store custom flags along with the password without additional rows in the database. Things such as the length of the raw password etc.*
  6. #4
  7. Code Monkey V. 0.9
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Mar 2005
    Location
    A Land Down Under
    Posts
    2,276
    Rep Power
    2039
    Originally Posted by nathan.fiscalet
    The original reason for the object storage method that I'm using above was to store custom flags along with the password without additional rows in the database. Things such as the length of the raw password etc.*
    That actually makes it less secure.

    Think about it... Someone hacks your site and gets the database. They have the hashed versions of the passwords, so they don't know what the real ones are, but you've given them a lot of clues there (how long the original password is is a huge example of this).

    Hash's are a whole lot more secure when that's all that's known. That way they could be pretty much anything and will take a whole lot more effort to crack. The more information that you give about the plain-text password, the less effort it will take to crack.
  8. #5
  9. Forgotten Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,105
    Rep Power
    9644
    Totally don't store any information about the password. Length? Bad idea. What rules were used to create it? Irrelevant once the password has actually been created.
  10. #6
  11. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2012
    Location
    Burb of Detroit, Michigan
    Posts
    107
    Rep Power
    95
    Talking about length, setting it to 255 characters in the database table would be a good idea.*
    PHP Code:
    /**
    ** We just want to hash our password using the current DEFAULT algorithm.
    ** This is presently BCRYPT, and will produce a 60 character result.
    **
    ** Beware that DEFAULT may change over time, so you would want to prepare
    ** By allowing your storage to expand past 60 characters (255 would be good)
    **/ 
    PHP: password_hash - Manual
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    9
    Rep Power
    0
    I like what you did with the password requirements. As everyone else said though, the rest of the program should be reduced to password_hash() with default salt
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    5
    Rep Power
    0
    Thanks guys, I'm gonna recode it a bit today. I do however have a quick question: Is there a reason why password_hash() is preferable to using a pbkdf2 method of hashing?
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    5
    Rep Power
    0
    The class is now identical excluding the hashing algorithm now using password_hash(), however the system is only storing the salt/hash in the database. Any time they are read from the database the password object is constructed using the "force" parameter. The flags are left for the developer when they initially construct the password to have quick access to verify that the password meets their requirements. Any other suggestions or critiques??
    (On a side note, I'm new to these forums and can't seem to find where I set my signature. Any help?)
  18. #10
  19. Not An Expert
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2015
    Posts
    404
    Rep Power
    3
    Hello,

    I don't believe that newer users are allowed to create signatures. It's part of our anti-spam system - nothing against you personally! It should unlock for you over time, as you make posts and contribute to the community.

    Once it unlocks you can go here: http://forums.devshed.com/usercp.php

    Then scroll down and look on the left for a pale green box that says "My Profile" in white in it. Under that should be text that reads "Edit Signature." Clicking on that will allow you to create a signature.
  20. #11
  21. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Posts
    5
    Rep Power
    0
    Originally Posted by markroberts
    Hello,

    I don't believe that newer users are allowed to create signatures. It's part of our anti-spam system - nothing against you personally! It should unlock for you over time, as you make posts and contribute to the community.

    Once it unlocks you can go here: http://forums.devshed.com/usercp.php

    Then scroll down and look on the left for a pale green box that says "My Profile" in white in it. Under that should be text that reads "Edit Signature." Clicking on that will allow you to create a signature.
    Thanks for the info, Mark.

    Comments on this post

    • Will-O-The-Wisp agrees : No problem - welcome to Dev Shed!
  22. #12
  23. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2015
    Location
    2530 Yorktown apartment , Houston ,Texas 77056
    Posts
    1
    Rep Power
    0
    Code:
    $pass       = 'somePassword';
    $hashed     = Crypt::encrypt($pass);
    $decrypted  = Crypt::decrypt($hashed);
    
    return [
        'pass'      => $pass,
        'hashed'    => $hashed,
        'decrypted' => $decrypted
    ];
  24. #13
  25. Forgotten Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,105
    Rep Power
    9644
    Originally Posted by Lemosys
    (code)
    And that's supposed to be... what, exactly?

IMN logo majestic logo threadwatch logo seochat tools logo