How to verify_password from a database
But I am not sure how to use it. How do I use it to get a user from a database? What’s the best way for me to verify a password with the following code:
public function get_user($username, $password)< $this->query = $this->conn->prepare('SELECT * from users WHERE username=:username AND password=:password'); $this->query->bindParam(':username', $username); $this->query->bindParam(':password', $password); $this->query->execute(); $this->retrieve = $this->query->fetchAll(PDO::FETCH_ASSOC); >
@tmyie — of course it does. but sadly many developers are still unaware that PHP 5.5 password_hash() actually exists
@Fluffeh — and one of the coolest things is the ability to use it in earlier versions of PHP than 5.5 via the 100% compatible userland library, giving yourself automatic upgradeability when you do progress to PHP 5.5
2 Answers 2
First of all, +1 for using PHP’s password functions for password hashing!
In contrary to normal hashing functions (such as md5() , sha1() , etc. — which should not be used for password hashing), password_hash() will produce a different hash from the same password every time, because it automatically generates a random salt for every hash. This is a great feature that makes your password hashes a lot safer, but it means that you cannot use password_hash() to hash the entered password, and use that hashed password in your SQL query (combined with the username) to retrieve the user.
Instead, just retrieve the user based on it’s username — and then compare the retrieved password hash with the entered password using password_verify() . This function is able to compare the entered password with the stored hash, even if the cost or algorithm have changed.
public function get_user($username, $password) < $this->query = $this->conn->prepare('SELECT * from users WHERE username=:username LIMIT 1'); $this->query->bindParam(':username', $username); $this->query->execute(); $user = $this->query->fetch(PDO::FETCH_ASSOC); if (password_verify($password, $user['password']) < // password is correct, return the user return $user; >else < // incorrect password return false; >>
Increasing the strength of passwords in the future
As I said before, the new password API allows to upgrade the strength of newly generated password hashes without breaking older ones. This is because the cost and the algorithm (as well as the salt, by the way) are stored within the hash.
It is advisable to increase the cost over time, as available hardware becomes stronger (decreasing the time it would take for an attacker to brute-force a password).
If you decide to do so, or if you decide to use another hashing algorithm, don’t forget to add a check using password_needs_rehash() in your login procedure. This way existing passwords will be re-hashed as well.
If the function (called with the hash from the database as a parameter) returns true, simply run password_hash() again and overwrite the old hash in the database with the new hash. This can obviously only be done when users log in, because that is the only time you should have access to the plain-text passwords.
How to verify password against database?
I went through many articles related to this topic, such as this: Using PHP 5.5’s password_hash and password_verify function Yet, I’m unsure if I’m hashing and salting the correct way or over doing it! I want to use my own salt and then hash. Both salt and hashed password stored in the database in two different fields. This is how I hash the password before storing into database
$cost = 10; $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.'); $salt = sprintf("$2a$%02d$", $cost) . $salt; //shall I remove this line and replace below PASSWORD_DEFAULT with PASSWORD_BCRYPT instead? $password = crypt($data['password'], $salt); $hash = password_hash($password, PASSWORD_DEFAULT);
Given that, I’m trying to verify the password as below: Somehow I feel that I’m complicating the process.
$salt=$row['salt'];//taken from db $hashAndSalt=$row['hashpword'];//taken from db $password="pwtester";//user keyed in password $newpassword = crypt($password, $salt); $newhash = password_hash($newpassword, PASSWORD_DEFAULT); if (password_verify($password, $newhash)) < echo"verified"; >else
$cost = 10; $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.'); $options = array('cost' => $cost,'salt' => $salt); $hash = password_hash($data['password'], PASSWORD_DEFAULT,$options);
$email = "test55@gmail.com"; $uid= '555ca83664caf'; $sql = "SELECT *FROM authsessions WHERE email =:myemail AND useruuid =:uid"; $statement = $pdo->prepare($sql); $statement->bindValue(':myemail', $email); $statement->bindValue(':uid', $uid); $statement->execute(); while( $row = $statement->fetch()) < echo "salt ".$row['salt']."
"; echo "hashpassword ".$row['hashpword']; > $salt=$row['salt']; $hashAndSalt=$row['hashpword']; $password="test55"; $newhash = password_hash($password+$salt, PASSWORD_DEFAULT); if (password_verify($newhash, $hashAndSalt)) < echo"verified"; >else
validate password with preg_match in php
I need to validate passwords. I currently use: preg_match(«/^[a-z0-9_-]*$/i», $pass) . I would like to add length to this. My mysql table is set up like this : userpassword varchar (40) NOT NULL, . So between 6 and 40 characters. And I would like to allow all characters that are not dangerous to put in my db.
First thing that comes to my mind from reading your question is that you don’t encrypt passwords. php.net/md5
@andre matos: MD5 is not an encryption, it’s a cryptographic hash function. But you’re right: The password should not be stored in plain text.
Don’t limit your passwords simply to alphanumeric characters. It’s better to enforce a set of rules such as minimum of 3 upper-case, minimum of 3 lower-case, minimum of 2 numeric, minimum of 1 non-alphanumeric (alter rules to suit your personal preferences), but insisting on non-alpha does force the brute-force attacks to do more work. When your password is in the database, it should be a hashed (and salted) value anyway, so the actual characters are irrelevant to database storage
5 Answers 5
Imposing arbitrary complexity rules on passwords is very user hostile and does not improve security substantially. Don’t do it.
Here’s why I think the above statement is true:
- If I want to use your website with «123456» as my password, it’s my problem. Let me roll with it.
- You may impose a minimum length (e.g. 6 characters), but not a maximum. If I want to cite the entire John Maynard as my password, let me roll with it.
- Your idea of a «secure» password might not be everybody else’s idea.
- People might use password generators that do not automatically comply with your rule set. Don’t annoy them by not accepting their password for no other reason than not containing enough/or too many «special characters».
- You must hash your customer’s passwords with a decent hashing algorithm plus a random hash salt, different for every user. Only store hash salts and hashes in the database, never store clear text passwords.
- Once hashed, even a lame password is reasonably secure against theft/cracking. Implement security against brute-force attacks (time-based lock-outs, IP-based lock-outs, password locking with e-mail handshake to retrieve a locked account).
So your password validation process goes like this
- New user? Create user record with username and random salt (never change the salt value for that user)
- Returning user? Fetch salt from DB, re-hash his password, compare result to hash in DB.
- Never store the user’s password anywhere physically and use HTTPS to transmit it.
- If you do not want to do something like the above, think about using OAuth with your site. May not be easy either, but you do not have to worry about password security anymore and your users have one less password to remember.
For the sake of the argument, this regex will do what you ask. If you’re still desperate to do it.
@SuperSpy: Only the password transfer needs to bee HTTPS, not the entire site. Anyway, there’s still risk to have your session cookie stolen by tools like FireSheep if not all of your data transfer is encrypted. But not transmitting the password in clear text should be the least to do.
You should validate your passwords by ensuring they are secure, not that they’re insecure.
public function password_is_secure($password) < // quick obvious test if (is_numeric($password)) < // FAIL: 'Your password cannot be all numbers.' >elseif (strlen(count_chars($password, 3)) < 4) < // FAIL: 'Your password needs to have a variety of different characters.' >// levenshtein distance test (test similarity to common passwords) $name = $_POST['name']; $email = $_POST['email']; $badPasswords = array($name, $email, 'password', 'password1', 'password123', '1234abcd', 'abcd1234', '12345678', '1234567890'); foreach($badPasswords as $bad) < if (levenshtein($password, $bad) < 6) < // FAIL: 'Your password is too similar to your name or email address or other common passwords.' >> return true; >
Plug-in more appropriate «getters» for $name and $email and setup how you want to handle passing error messages, and the above method will do you some justice. You can «tune» it by altering the allowed Levenschtein distance (currently 6).
I would also recommend extending the list of $badPasswords to include a bunch of the most common passwords.
And for the love of some deity, salt and hash your passwords before you store them in the database.
+1 for the «check against most common passwords» hint. I was hesitant of writing this because I said «let me roll with ‘123456’» in my answer, but it’s probably not too much to forbid the really common ones.
The issue with numeric passwords is that users don’t realize that they’re exponentially easier to brute-force. At 6-digits, there are only 1M possibilities, vs 6 alphanumeric characters at 56 billion possibilities. At 8-digits it’s at a still-insecure 100M possibilities, vs 8 alphanum chars for 217 trillion possibilities.
Anybody worth their salt doing a brute-force attack (especially against a table of users) is going to start with the area with the highest probability of successful results. If they’re not using a dictionary, it’s all about the numbers.
I would like to give you to the following tips:
OpenID, Facebook Connect
you should not be storing passwords in your database. Use OpenID via the really easy LightOpenID and for the following reasons:
- When you use an openid provider then you DON’T store the passwords(incorrectly) so there can NOT be passwords stolen.
- Your user do NOT have to create yet another account to login into your site.
- The good OpenID providers have SSL in place so your passwords is NOT sent over the wire in plain-text.
Better not
P.S: for the fun of it. Somebody else asked me on stackoverflow.com if his loginsystem was any good/safe(it was NOT), so I wrote a SAFE login system. If you use SSL(and if I did NOT miss any vulnerabilities, but I do NOT believe I did).
- use phpass to store your passwords SAFE.
- Use SSL to make sure your passwords are NOT sent over the wire in plain-text.
- Don’t use mysqli, but use PDO instead. That way you are already protected against XSS when use prepared statements. This is article:»Why you Should be using PHP’s PDO for Database Access» is an excellent introduction.
length / safe
I would like to add length to this. My mysql table is set up like this : userpassword varchar (40) NOT NULL,. So between 6 and 40 characters. And I would like to allow all characters that are not dangerous to put in my db.
I do NOT like regexp because they are overly complicated(I only use as last effort). I would advise you just to use an up to date version of PHP(>5.2.0) which has protection in place to make it safe against XSS.
The filter extension is enabled by default as of PHP 5.2.0. Before this time an experimental PECL extension was used, however, the PECL version is no longer recommended or updated.
Next validating length is as simple as using strlen function.