Why do you need to store the password at all, even an encrypted version? Is your site accessing a 3rd-party API in the backend that does HTTP Basic auth or something?
Unfortunately there's no definitive answer to your question. "Appropriate" means different things to different people. And with security questions, I'm not sure it's ever possible to be "appropriate enough" or "secure enough." That said, here's how I handle logins and password security. In my database's users
table, I have 3 login-related columns:
- username
- salt
- passwordHash
The username
column is in plain text. The salt
column is a 64-character string of randomly chosen alphanumeric characters. The passwordHash
column is the user's password concatenated with their salt
value, and then irreversibly hashed. I use sha256 for my hashes. The salt is 64 characters because that's what sha256 produces. It's good to have a salt value at least as long as the hash in order to produce enough variability in the hashed string.
When the user submits the login form, I do a database query for the username. If the username isn't found, I show an "Invalid username and/or password" error to the user. If the username is found, I concatenate the salt with the password, hash it, and see if it equals the passwordHash
value. If not, the user is shown the exact same error.
It's good to show the exact same error message regardless of whether the username was wrong, or the password was wrong, or both. The fewer clues you give to a hacker, the better. Also, whenever a user changes their password, I give them a new salt
too. It's really easy to do this at that point in time, and it keeps the salt values a little fresher.
This system of having a different salt per user is called dynamic salting. This greatly complicates a hacker's job if they try to use rainbow tables to reverse-engineer your users' passwords. Not to mention that storing passwords in irreversibly hashed form goes a very long way toward keeping anyone from determining a user's password, even if they have access to the database and the PHP code.
This also means if your user forgets their password, there's no way to retrive it. Instead, you write your system to just reset it to a new randomly-determined value that is sent to them along with strong encouragement to change their password as soon as they log in again. You can even write your system to force this upon the next successful login.
I require passwords to be at least 8 characters. Ideally, it should also include numbers and special characters, but I haven't decided I should require this yet. Maybe I should!
To protect against brute-force attacks, I keep track of all failed logins during the previous 10 minutes. I track them on a per IP address basis. After 3 failed login attempts, the system uses the sleep()
function to delay responding to further login attempts. I use a block of code like this:
$delay = ($failedAttempts - 3);
if ($delay > 0) {
sleep($delay);
}
IMHO this is much better than locking users out of their accounts after a hard number of failures. It reduces the number of customer support inquiries you'll get, and it's more graceful for legitimate users who simply can't remember their own passwords. Brute-force attacks need to do many attempts per second in order to have any sort of efficiency, so delaying on an n = x
basis keeps them from getting very far at all.
Login sessions are tracked with PHP sessions. Call session_start()
when every page on your site is loaded. (This is really easy if you have a common header.php
file.) This makes the $_SESSION variable available. When a user successfully logs in, you can use this to store whatever info your site needs in order to know the user is logged in. I typically use their User ID, Username, and maybe some other details specific to the site. But I don't include the password or a hash of it here. If somehow a hacker got into the user's session data, which is stored on your server, they still wouldn't have a chance at finding the user's password this way.
Logging out happens when one of 2 things occur: Either 1) The user's session cookie is deleted, such as by clearing the browser cache or sometimes just by closing the browser window, or 2) Your server deletes their session data. You can force the latter to happen via calling session_destroy()
when the user presses your "Log out" button on your site. Otherwise you could make sessions automatically expire after a certain period of time. This may involve tweaking your session.gc_*
params in php.ini
.
If you absolutely must know the user's password after the initial login phase, you can store it in $_SESSION
. Do this IF AND ONLY IF your site requires an SSL connection and you've made it so the site won't work without one. This way the password is encrypted and so is protected against packet sniffing. But know that it's a security risk if a hacker gets access to your server's session data.