Designing a secure auto login cookie system in PHP
The more secure you want this infamous cookie, the more trouble it's going to be for you. If your users should be particularly secure, you will have to go with the most troublesome approach.
You should only accept this cookie with https if you want to be as secure as possible. If the cookie is accepted over http, it can be sniffed and stolen.
I would recommend that the cookie have no user data at all (a token, as you suggested). This will, unfortunately, require another table. When a user logs in and chooses "keep login," create an entry in this table. The entry can be any meaningless value (such as md5(uniqid('', true));
. This token can be unique in the DB and mapped to a user's ID.
When a user visits your website, you can check the value of that cookie and get the user it belongs to and log them in. At this point, you destroy the old token and create a new one. "Destroy" can mean many things. You can delete it from the DB entirely or have a flag that disables the token. You may want to allow the same token to be used multiple times in case the cookie is received but the authentication doesn't go through for some reason, but I think this is insecure. You may also want to store the timestamp of the token and only accept it if it's been some limited period of time (30 days for example).
As your friend points out, you can store other information such as user agent, IP address, etc., but these may change even with the same browser being used (especially with mobile) and if a user's persistent login is not accepted because of this, it could be jarring and inconvenient to them.
If you really don't want to create another table, then you will have to store some way to acquire the user's ID from the cookie value. This is less secure.
Creating a secure login using sessions and cookies in PHP
There is no such thing as secure cookie UNLESS it's transmitted over SSL only. It can be mitigated some when using a persistent non-session cookie (like remember me), by doing exactly what you're doing, but not in the same way you're thinking of doing it.
You can indeed store server variables such as the user-agent, the ip address and so forth (and even JavaScript variables), but they are only good for validating that the persistent cookie data matches the client's new connection. The ip address isn't a good idea except when you know that the client (like you only) isn't going to change on every page load (a la AOL).
Modern web browsers and 3rd party services like LastPass can store login credentials that only require a key press (and sometimes not even that) to send the data to the login form. Persistent cookies are only good for those people who refuse to use what's available otherwise. In the end, persistent, non-session cookies are not really required anymore.
Secure Autologon (keep me logged in) - using Cookies & PHP
If anyone accessed there system, he could steal the whole base of users, so you shouldn't worry about stealing your private key.
Anyway, if you don't want to keep user information in cookies, you could keep session keys in database: [user_id], [session_key], [expire_date]
(records could be multiple to work with different computers). Cookie: session_key="mn2..23j"
. Then you should check if cookie matches database record.
Auto login security with PHP, how and why
There are two parts to this:
- Making sure your sessions are secure. Read this for a primer on session security, with configuration examples. Spoiler: HTTPS is mandatory, make sure you're using a CSPRNG for session identifiers, and you're regenerating session IDs during privilege escalation (and/or regularly).
- Safely implementing a cookie-based authentication token that will transparently re-authenticate the user when they access your website with a valid cookie but without an active session. I've previously written extensively abut side-channel-resistant persistent user authentication.
The second part is more interesting, because many people stop at the "generate a random token, store in a cookie, and then look it up in the database" step.
You're safe to assume that your heavily-optimized database query isn't comparing strings in constant-time, like you need to be in order to prevent timing attacks.
The solution is to break your token into two pieces: One for the database lookup (timing-leaky), and the other for comparing in constant-time.
- The first piece (called the selector) is stored/indexed in your database table.
- The second (called the verifier) piece is stored in plaintext in your user's cookie, but is hashed (e.g. SHA256) in the database.
After retrieving the token based on the selector, you compare the hash of the verifier provided by the user with the stored hash, using hash_equals().
Each long-term authentication token should only be used once. Afterwards, a replacement should be issued to the end user. Logging out should clear the cookie, while the session should expire when the browser is closed.
This is only secure if you're using HTTPS with HSTS, and your cookies are set to HTTPS-only! (This means secure
and httpOnly
are both set to true
.)
You can see an example of this system in action here and here.
What is a relatively secure way of using a login cookie?
I think I've found a clever solution!
Advantages of this (complicated?) script:
- When the user successfully logs in
with Remember Me checked, a login
cookie is issued in addition to the
standard session management
cookie.[2] - The login cookie contains the user's username, a series identifier, and a token. The series and token are unguessable random numbers from a suitably large space. All three are stored together in a database table.
- When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
- If the triplet is present, the user
is considered authenticated. The used
token is removed from the database. A
new token is generated, stored in
database with the username and the
same series identifier, and a new
login cookie containing all three is
issued to the user. - If the username and series are
present but the token does not match,
a theft is assumed. The user receives
a strongly worded warning and all of
the user's remembered sessions are
deleted. - If the username and series are not
present, the login cookie is ignored.
I've made a table in the database with the following information:
session | token | username | expire
The remember me cookie will have this setup:
$value = "$session|$token|$userhash"; //Total length = 106
Session
will be a string of 40 (sha1)
characters.Token
will be a string of 32 (md5)
characters.Userhash
in the cookie will be a
string of 32 (md5 of username)
characters.Username
in the database will be the
normal username.Expire
will be now + 60 days.
The script:
if(isset($_SESSION['check']) || $_SESSION['check']){
//User is logged in
}else if(isset($_COOKIE['remember']) && strlen($_COOKIE['remember'])==106){
//THERE is a cookie, which is the right length 40session+32token+32user+2'|'
//Now lets go check it...
conncectdb(); //Sets connection
//How do I protect this script form harmful user input?
$plode = explode('|',$_COOKIE['remember']);
$session = mysql_real_escape_string($plode[0]);
$token = mysql_real_escape_string($plode[1]);
$userhash = mysql_real_escape_string($plode[2]);
$result = mysql_query(" SELECT user
FROM tokens
WHERE session = '$session'
AND token = '$token'
AND md5(user) = '$userhash';")
if(mysql_num_rows($result)==1){
//COOKIE is completely valid!
//Make a new cookie with the same session and another token.
$newusername = mysql_result($result,0,0);
$newsession = $session;
$newtoken = md5(uniqid(rand(), true));
$newuserhash = md5($username);
$value = "$newsession|$newtoken|$newuserhash";
$expire = time()+4184000;
setcookie('remember', $value, $expire, '/', 'www.example.com', isset($_SERVER["HTTPS"]), true);
mysql_query(" UPDATE tokens
SET token='$newtoken', expire='$expire'
WHERE session = '$session'
AND token = '$token'
AND md5(user)='$userhash';");
//Set-up the whole session (with user details from database) etc...
} else if(mysql_num_rows(mysql_query("SELECT user FROM tokens WHERE session = '$session' AND md5(user) = '$userhash';"))==1)){
//TOKEN is different, session is valid
//This user is probably under attack
//Put up a warning, and let the user re-validate (login)
//Remove the whole session (also the other sessions from this user?)
} else {
//Cookie expired in database? Unlikely...
//Invalid in what way?
}
} else {
//No cookie, rest of the script
}
Advantages of the script:
- Multiple login. You can create new
sessions for each computer you're on. - Cookie and database will stay clean.
Active users renew there cookie every
login. - The session check at the beginning
ensures that the database will not
get useless requests. - If an attacker steals a cookie, it
gets a new token, but not a new
session. So when the real user visits
the website with the old(invalid)
token but WITH a valid user-session
combination the user gets a warning
of the potential theft. After
re-validating by logging in a new
session is created and the session
the attacker holds is invalid. The
re-validating ensures the victim
really is the victim, and not the
attacker.
Reference: http://jaspan.com/improved_persistent_login_cookie_best_practice
how to create a secure php login system, allowing for keep me logged in functionality?
First: Configure the session.cookie_lifetime
directive, either in php.ini, configuration files, or via session_set_cookie_params()
.
Next, store the username and the hash value of the password in the session, and validate that login on every page. As long as it's still valid, they get to stay logged in.
The session cookie's natural expiration should generally keep things tidy, as you won't have anyone getting logged out in the middle of their session (if the stars aligned for it, of course) if they keep it active. Failing that, though, I'd consider eCartoth's solution a close second, as you could just add a second line to the if statement:
if (my_validate_user_function($_SESSION['username'],$_SESSION['passhash'])
&& $_SESSION['deathstamp'] > time()
) {
// user is logged in again, oh boy!
}
else {
// send in the death robots
header('Location: /login.php',true,302);
}
EDIT: One thing you might want to consider is session fixation and/or session hijacking. In order to prevent that, I'd recommend one (or both) of two solutions:
- store the user's IP address in the session
- use
session_regenerate_id()
after every successful login attempt.
What's the best method to protect login cookie data in PHP?
Don't store sensitive information in cookies. Store a session ID hash to connect the logged in user with their account.
Related Topics
How to Send Cookies with File_Get_Contents
Php: Check Who Had Read Sent Email
Warning: "Continue" Targeting Switch Is Equivalent to "Break". Did You Mean to Use "Continue 2"
PHP Date Time Current Time Add Minutes
How Validate Unique Email Out of the User That Is Updating It in Laravel
Can a User Alter the Value of $_Session in PHP
Any Way to Keep Curl's Cookies in Memory and Not on Disk
How Many Days Until X-Y-Z Date
How to Use C++ Binaries from PHP
Facebook API/PHP - How to Change a User's Profile Image via Fb Graph API
Regex/Domdocument - Match and Replace Text Not in a Link
Create New Product Attribute Programmatically in Woocommerce
Automatically Refresh Token Using Google Drive API with PHP Script