Designing a Secure Auto Login Cookie System in PHP

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:

  1. 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).
  2. 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:

  1. store the user's IP address in the session
  2. 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



Leave a reply



Submit