Considerations When Building an Authentication System (Part 2: Cookies)

Please read Part 1 as it will give you some background thoughts.

Part 1 of this instalment covered the use of Sessions (cookies and data) and how to use them securely. This part will cover cookies and the importance cookies play in authentication systems.

Table of Contents for Part 2: Cookies


Cookies (Remember Me)
To store a visitor’s login credentials for longer than 20min (the PHP session default), we need to rely on the browser’s cookies. In doing so, we need to consider the security of cookies and how best to reduce opportunities by hackers to gain access to a visitor’s login credentials.

It is not a good idea to store the user’s username and password as plain text in a cookie in case their browser is compromised or an XSS vulnerability makes those sensitive details available to a hacker. This would allow a hacker to copy the visitor’s login credentials, which could also gain them access to other websites the visitor uses.

To reduce the risk of your visitor’s becoming a victim:

  1. Cookies containing sensitive data, such as login credentials should only be available across an SSL (HTTPS) connection
  2. Block JavaScript from having access to the browser’s cookies
  3. Passwords should not be stored in cookies as plain text. Use a token or hash/encrypted the password

I’m going to cover each of them below.

RULE: Cookies must always be sent over HTTPS
To ensure that cookies storing sensitive data do not accidentally get broadcast across a non-secure connection (HTTP), ensure cookies are created using the ‘secure’ flag.

Usage: setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
http://php.net/manual/en/function.setcookie.php:

<?php
// Create a secure cookie ‘myVar’ with the value ‘hello word’ for one day
setcookie(‘myVar’, ‘hello world’, time()+86400, “/”, “.mydomain.com”, true);
?>


RULE: Block JavaScript from Accessing Cookies
If the JavaScript within your project does not require direct access with your website’s cookies, then the client (JavaScript and the browser) should be blocked from having access to your website’s cookies. This can be done when a cookie is created, by setting the ‘httponly’ flag:

setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
http://php.net/manual/en/function.setcookie.php:

<?php
// Create a secure cookie ‘myVar’ with the value ‘hello word’ for one day that is
// only available to server-side code, not the client-side (JavaScript)
setcookie(‘myVar’, ‘hello world’, time()+86400, “/”, “.mydomain.com”, true, true);
?>

Note, the ‘httponly’ flag is not interpreted in all browsers yet.
https://www.owasp.org/index.php/HttpOnly
http://stackoverflow.com/questions/5313290/when-linking-to-an-external-js-file-isnt-this-a-security-risk

RULE: Store the Password Securely in the Cookie (Encrypted or Token)
There are a couple of ways of securely storing a visitor’s password in a cookie. These are:

1. Encrypt password – simply encrypt the password at the server before saving it as a cookie and making it available over the wire. This will ensure that if a hacker manages to get a hold of a victim’s cookie, they cannot use the details on any other websites the visitor uses the same credentials on.

2. Hash password – hashing the password follows the same principle as encrypting the password, except that the decryption key is not stored on the server. The password is put through an md5() or sha1() for example:

<?php
// The user’s password with a salt is passed into PHP’s md5() method.
$hash = md5(‘this-is-your-salt’.$password);
?>

It’s very important to use a salt when making a hash of a password. There are plenty of websites that provide the service of decrypting md5 hashes by creating large databases of mappings between common words (eg flower) and md5 hashes. Adding a salt ensures that a password is not a common word.

3. Token – a token offers many benefits over encrypting or hashing the password and storing it in a cookie. A token is a randomly generated key that is unique to a given visitor. When the visitor authenticates, a token is generated, stored in the visitor’s database record and the token is placed in a cookie in the visitor’s browser. Each subsequent request, the authentication system verifies authenticity using both the visitor’s identy (username) and token.

The advantages of using a token are:

  1. A visitor’s authenticated session can be revoked by removing the token from the database. An example of this is with Gmail where it’s possible to log yourself out of another computer you’re currently logged in on
  2. A token can have an expiry date set in the database, ensuring that after a reasonable amount of time, the token should be updated. Simply relying on the cookie’s expiry date is not a good idea, as a visitor can simply extend the cookie’s expiry date
  3. More difficult to hijack a visitor’s session because the token regularly changes, opposed to an encrypted or hashed password
  4. Depending on the implementation, a token can be used to ensure the visitor is logged into one computer/session only


Populating the Cookie with Session Data
When a visitor authenticates using the login form, the following process should occur:

  1. Visitor inputs are sanitised/validated
  2. Details are verified against details stored in database
  3. If valid, username stored in session data for short-term browser persistence (approx 20 mins)
  4. If valid, a cookie is created to store the visitor’s username and token for long-term browser persistence (eg 7 days)

Within 20min of the initial login, when the visitor requests a page requiring authentication, the following process is followed when the page is requested:

  1. Browser sends cookies to server
  2. Server uses the session cookie to load the session data ($_SESSION)
  3. If session data exists, display sensitive information

If the user hasn’t used the session for 20min and then requests a page, the following process is followed:

  1. Browser sends cookies to server
  2. Server uses the session cookie (which may not exist) to load the session data
  3. The session data does not exist on the server because the session was created more than 20mins ago
  4. Check to see if a cookie exists with the username and token
  5. If the username and token exists, verify against details stored in database
  6. If the cookie data and the details stored in the database match, display sensitive information
  7. Optionally, update the session data with the data stored in the cookie


RULE: Ensure Cookies cannot be Stolen from Subdomains
Cookies are only available on subdomains of your choosing. If you do not want cookies shared between subdomains, ensure that you do not prefix the domain with a period.

<?php
// Prefix domain with a period (‘.’) to give all subdomains access to the cookie
setcookie(‘myVar’, ‘hello world’, time()+86400, “/”, “.mydomain.com”);
?>

The above example will give the following domains access to the ‘myVar’ cookie:

If you want to limit your cookie to a single subdomain, simply enter it as the domain of the cookie:

<?php
// Only www.mydomain.com has access to the cookie.
setcookie(‘myVar’, ‘hello world’, time()+86400, “/”, “www.mydomain.com”);
?>

Should a visitor arrive at your website via “mydomain.com” and not “www.mydomain.com”, they will not gain access to the cookie.

RULE: All sensitive data should travel over HTTPS only
As discussed in detail earlier, cookies (including session cookies), forms and web pages containing sensitive data should only be transported over HTTPS and never over the non-secure HTTP.

REALLY??!! How easy is it for a hacker to hijack your Facebook or Gmail account when used over the insecure HTTP? Very easy! Check out this Firefox plugin, Firesheep: http://codebutler.com/firesheep

RULE: Don’t trust session data and cookies for ultra-sensistive data
It’s important to consider the process when a visitor accesses or request to change very sensitive data. The visitor should be prompted to re-enter their password. Amazon has taken this approach and it no doubt reduces the damage caused by session hijacking and for those instances where a visitor forgets to logout of a public computer at their local library.

Part 3: Database
Part 3 will cover database security and steps that should be taken to ensure visitor’s data is not compromised. This is the final instalment of this series.
Considerations When Building an Authentication System (Part 3: Database)

About the Author
Brett is the Lead Web Developer at BBC.com working on a number of products, such as the BBC International Homepage, News, Sport, Travel and the back-end work on the iPhone and iPad applications.

Advertisements
Posted in PHP, Zend Framework, Zend_Filter, Zend_Session

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: