[ACCEPTED]-PHP best practices for user authentication and password security-authentication
OpenID is a method to authenticate users based 7 on their existing accounts on common web 6 services such as Yahoo, Google and Flickr.
Logins 5 to your site are based on a successful login 4 to the remote site.
You do not need to store 3 sensitive user information or use SSL to 2 secure user logins.
A current PHP version 1 of the library can be found here.
Implementing user authentication securely without relying on a framework (or third-party 37 library, such as OpenID) to do it for you 36 is not a trivial undertaking.
At a 10,000 35 foot overview, you have to decide:
- Do you have usernames, email addresses, or user IDs as a primary selector?
- How should you store passwords? PROTIP:
password_hash()
or scrypt are the way to go. - How should you handle "remember me" checkboxes? There are a lot of bad strategies for this on the Internet. Treat every one of them with skepticism, because they might introduce vulnerabilities into your application.
- How should the application handle users who forget their password?
The information 34 in this answer is relevant and up-to-date 33 as of May 9, 2015 and might be obsoleted 32 by the conclusion of the password hashing competition
Primary Selectors
In general, usernames 31 and email addresses are better than ID numbers.
There 30 should be no security requirement to keep 29 usernames secret, because in practice they 28 will be leaked when someone tries to register 27 anyway.
You can decide whether or not to 26 treat email addresses as a secret. Users 25 generally like not being exposed to spammers, scammers, and 24 trolls.
Password Hashing
You should use password_hash()
and password_verify()
unless you are 23 sufficiently experienced with writing cryptography 22 libraries to go above and beyond.
Beyond Bcrypt
Sometimes 21 developers like to get creative (e.g. adding 20 a "pepper", which usually means 19 pre-hashing or HMACing passwords with a 18 static key) and go beyond the standard implementations. We 17 ourselves have done this, but very conservatively.
For 16 our internal projects (which have a much 15 higher margin of security than most people's 14 blogs), we wrote a wrapper around this API 13 called PasswordLock
that first hashes a password with 12 sha256
, then base64 encodes the raw hash output, then 11 passes this base64-encoded hash to password_hash()
, and 10 finally encrypts the bcrypt hash with a properly-implemented encryption library.
To 9 reiterate, instead of peppering, we encrypt our password hashes. This gives us more agility in 8 case of a leak (we can decrypt then re-encrypt 7 because we know the key). Additionally, we 6 can run our webserver and database on separate 5 hardware in the same datacenter to mitigate 4 the impact of a SQL injection vulnerability. (In 3 order to start cracking hashes, you need 2 the AES key. You can't get it from the database, even 1 if you escape to the filesystem.)
// Storage:
$stored = \ParagonIE\PasswordLock\PasswordLock::hashAndEncrypt($password, $aesKey);
// Verification:
if (\ParagonIE\PasswordLock\PasswordLock::decryptAndVerify($password, $stored, $aesKey)) {
// Authenticated!
}
Password Storage with PasswordLock
:
hash('sha256', $password, true);
base64_encode($step1);
password_hash($step2, PASSWORD_DEFAULT);
Crypto::encrypt($step3, $secretKey);
Password Verification with PasswordLock
:
Crypto::decrypt($ciphertext, $secretKey);
hash('sha256', $password, true);
base64_encode($step2);
password_verify($step3, $step1);
Further Reading
I use OpenID .
But like stackoverflow I use the 2 Google project openid-selector to do the heavy lifting.
Demo 1 Page here.
The obvious advantages (of OpenID) are.
- You don't need to be a security expert.
- Users trust the big sites with their info.
- You can request things like (nickname etc) but user has to opt in.
- You don't need to worry about:
- registration processes
- lost/forgotten password
A lot of great answers here, but I feel 9 like it's worth saying this--do NOT try to re-invent the wheel in this case! It 8 is extremely easy to screw up user authentication 7 in a wide variety of ways. Unless you really need 6 a custom solution, and have a firm knowledge 5 of security schemes and best practices, you 4 will almost certainly have security flaws.
OpenID 3 is great, or if you're going to roll your 2 own, at least use an established library 1 and follow the documentation!
PHPass is a lightweight, variable cost password 9 hashing library using bcrypt.
Variable 8 cost means that you can later turn up the 7 'cost' of hashing passwords to seamlessly 6 increase security without having to invalidate 5 your previously hashed user passwords.
The 4 field size used for hash storage is constant 3 even when increasing 'cost' due to increasing 2 not the size of the hash, but the number 1 of iterations required to produce it.
Login using HTTP AUTH
- Using Apache: http://httpd.apache.org/docs/2.2/howto/auth.html
- It is also possible with IIS and other web servers.
Once authenticated, for PHP, you just have 45 to use $_SERVER['PHP_AUTH_USER']
to retrieve the username used during 44 authentication.
This can be a faster and, sometimes, more 43 flexible solution than handling the solution 42 at scripting level provided that limited 41 information is needed regarding the user 40 as all that is made available to you is 39 the username used to login.
However, forget 38 about integrating your authentication in 37 an HTML form unless you implement a full 36 HTTP AUTH scheme from within your scripting 35 language (as described below).
Rolling your own HTTP AUTH within your scripting language
You can actually 34 extend HTTP Basic Auth by emulating it in your scripting language. The 33 only requirement for this is that your scripting 32 language must be able to send HTTP headers 31 to the HTTP client. A detailed explaination 30 on how to accomplish this using PHP can 29 be found here: (see more on PHP and HTTP AUTH).
You can expand on the 28 article above using a typical authentication 27 schema, file store, even PHP sessions or 26 cookies (if information isn't needed to 25 be persistent), giving you much more flexibility 24 when using HTTP AUTH, yet still maintaining 23 some simplicity.
Downsides to HTTP AUTH
The main downside to HTTP 22 auth is the complications that logging out 21 can have. The main way to clear the user's 20 session is to close the browser, or pass 19 off a header with 403 Authentication Required. Unfortunately, this 18 means the HTTP AUTH popup comes back on 17 the page and then requires users to either 16 log back in or hit cancel. This may not 15 work well when taking usability into consideration, but 14 can be worked around with some interesting 13 results (ie. using a combination of cookies 12 and HTTP AUTH to store state).
HTTP AUTH 11 popups, session, and HTTP header handling 10 is determined by browser implementation 9 of the standard. This means that you will 8 be stuck with that implementation (including 7 any bugs) without the possibility of workaround 6 (unlike other alternatives).
Basic auth also 5 means auth_user and password show up in 4 server logs, and then you have to use https 3 for everything because otherwise username 2 and password also go over the network on 1 every query in plain text.
It's important to separate the security 21 layer of your application from the rest 20 of it. If there's no distance between your 19 application logic and your communication 18 system, you are free to communicate insecurely 17 in one place and securely somewhere else. Maybe 16 you'll make a mistake and send a password 15 in an unencrypted cookie, or maybe you'll 14 forget to verify the user's credentials 13 for one step. Without a 'right way' to communicate 12 with the user, you're sure to make a mistake.
For 11 example, let's say this is how you verify 10 users now:
user_cookie = getSecureCookie()
if (user_cookie.password == session_user.password) {
do_secure_thing()
...
}
If a vulnerability is discovered 9 in getSecureCookie(), and you use this code 8 to verify users throughout your application, you 7 might not find all the instances of getSecureCookie() that 6 need to be fixed. If, however, you separate 5 your logic from your security:
if (userVerified()) {
do_secure_thing()
...
}
... you will 4 be able to quickly and easily re-secure 3 your application. Give yourself a 'right 2 way' to do security, and you will be far 1 less likely to make a major security blunder.
Best authentication is to utilize multi-factor 6 authentication, ideally a token-less version 5 on all security sensitive log-ons.
Password 4 protected and easier to use with high reliability 3 and security. There are several available 2 beyond EMC/RSA. I prefer SwivelSecure's 1 PINSafe.
Igor S
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.