How to Create Totally Secure Cookies

Securing cookies and sessions is vital to keeping an application secure. Many tutorials have been written on the subject, but as the internet (and browsers loading it) evolve so do the methods you can use to keep your application secure.

In this article we’re going to break down the various components of a cookie and what they mean for security. This will include limiting the cookie to certain domains and paths on those domains, choosing what information to store, and protecting the cookie from cross site scripting exploits. In a second article we will go into more depth in how to protect everyone’s favorite cookie, the session ID.

How Cookies Work

Cookies are simply key/value pairs that let us get around HTTP being a stateless protocol. When a developer has data they wish to last for more than one connection they can use cookies to store that data on the client side. While this tends to get handled by the programming language being used it is accomplished using HTTP headers.

When the server wants to set a cookie it passes back a header named “Set-Cookie” with the key-value pair and some options.

On subsequent requests the client will send along its own header to let the server know the name and value of its stored cookies. The server will not continue to send back the cookies, it will only send them if there is a change.

You can see all the headers for yourself using the LiveHeaders plugin for Firefox.

The Problem

This data is completely in control of the client- it is trivial to change the values of a cookie. That means that, just like post and get data, all cookie data must be validated in some way. At the same time you’ll want to avoid storing sensitive information, such as passwords, as cookies are stored in cleartext and anyone with access to the computer later can easily pick those up (I know of at least one security forum that was hacked in this way). It is also important to note that HTTP does not encrypt the headers in any way. If the connection isn’t over SSL then it will not be protected from snooping eyes.

Session cookies are no different than any other cookie- their value is just a simple ID. Those IDs are susceptible to all of the same limitations as other cookies. The real power behind sessions happens server side, where the ID is used to pull out data stored on the server. This has many benefits over storing data directly into the cookie itself- data can’t be manipulated by the user, large amount of data can be stored without having to send it back and forth with each request, and you can store data you otherwise wouldn’t want the client to have access to.

Getting Started

The first step towards securing your cookie is to restrict that cookie to only your application. This is especially important in environments that support multiple sites or applications (the type of shared hosting you often see on corporate or university domains). By restricting the cookie to only the applications that need it you reduce the chances of it being sniffed while also keeping the cookie namespaces clear for other applications that use them.

There are three options that can be sent along when creating a cookie that, when used properly, will keep the cookie limited to only your application. Before setting these options you will need to ask yourself a few questions-

  • What parts of the website need access to the cookie?
  • Will the cookie need to work across sub domains?
  • Will the cookie need to persist if the user leaves an SSL portion of the site?

There is also a forth option used by newer browsers to restrict access to cookies by javascript.

As you will see, how exactly to restrict the cookie really does depend on the exact purpose for that cookie. A banking or ecommerce site may restrict their cookies to only SSL, while a blog or news aggregator may want to leave things more open.

Cookie Options

Send The Cookie To Only Your Application

The Path argument specifies what paths on the site to send the cookie. The default value of “/” means every request will get the cookie, while “/forums/” would limit the cookie to just that path. This path is going to be based on the actual URL the browser uses, before any mod_rewrite or other URL mapping.

Don’t Share With Sub Domains

The Domain option allows you to specify whether or not to send the cookie to subdomains. Setting “www.example.com” will mean only the exact domain “www.example.com” will be matched, while “.example.com” will also match again any subdomaim (forums.example.com, blog.example.com).

Require a Secure Connection

Using the Secure option you can tell the browser (or other http clients) to only send the cookie over SSL connections. This means the cookie will not be available to any part of the site that is not secure will not have access to the cookie, but it also makes it much less likely that you’ll accidentally send the cookie across as cleartext.

Protect Against XSS Exploits

This HttpOnly flag is used to tell the browser that it should not allow javascript to access the contents of the cookie. This is primarily a defense against cross site scripting, as it will prevent hackers from being able to retrieve and use the session through such an attack.

The HttpOnly option is not by any means full proof. As a client-side defense mechanism it relies on browser support to work, but is only supported by a few browsers (Firefox 3+ and IE 7+, with partial support from Opera 9.5, IE6 and Chrome).

Configuring the Cookie

In PHP, setting the arguments for cookies is done through some optional arguments on the “setcookie” function:

setcookie( name, value, expire, path, domain, secure, httponly);

// Open
setcookie( 'UserName', 'Bob', 0, '/', '.example', false, false);

// Locked Down
setcookie( 'UserName', 'Bob', 0, '/forums', 'www.example.com', isset($_SERVER["HTTPS"]), true);

To change the cookie values for the session cookie requires the “session_set_cookie_params” function, which needs to be called before the session is started.

session_set_cookie_params($expire, $path, $domain, $secure, true);

// Open
session_set_cookie_params(0, '/', '.example', false, false);

// Locked Down
session_set_cookie_params('o, /forums', 'www.example.com', isset($_SERVER["HTTPS"]), true)

Summary

Cookies remain the basic method of identify tracking on most websites and keeping them secure is a vital part to keeping applications as a whole locked down and secure. In this article we went over four methods for protecting cookies on a general level.

When using cookies its important to remember to:

  • Limit the amount of sensitive information stored in the cookie.
  • Limit the subdomains and paths to prevent interception by another application.
  • Enforce SSL so the cookie isn’t sent in cleartext.
  • Make the cookie HttpOnly so its not accessible to javascript.

Please check out the second half of this series, where we’re going to take the next step with an in depth guide to securing sessions.

Free Workshops

Watch one of our expert, full-length teaching videos. Choose from HTML, CSS or WordPress.

Start Learning

Robert Hafner

Robert Hafner has been writing code and leading companies for as long as he can remember. He is heavily involved in open source and has released numerous projects, including many security and scalability related libraries. On the professional level he has acted as a dev/ops consultant for many companies and spent five years in an executive role at a leading security company. Robert can be found on LinkedIn, Github, Twitter and on his blog.

Comments

46 comments on “How to Create Totally Secure Cookies

  1. Pingback: How to Create Totally Secure Cookies « Raanan Avidor

  2. Great explanation of cookies and cookie properties. In the example security largely depends on enforcing SSL. But SSL only works on HTTPS, right? What about situations where HTTPS isn’t possible? What’s your take on encrypting cookie data?

  3. Using session cookies therefore should always be preferred over “normal” cookies. To secure session cookies, you can bind the session_id() to the unique combination of User_Agent and Remote_IP. Session hijacking can also be prevented by changing the session_id() of a session (using session_regenerate_id()) on a regular basis.

    • Thanks Philipp. Just an FYI, Robert has written another article about specifically securing Sessions. Should go live in a few weeks.

    • About binding the session_id() to the unique combination of User_Agent and Remote_IP.
      In large organisations, chances are that many users will share the same IP as internet access is regulated. I also use session regeneration, but I wonder if session regeneration wouldn’t be defeated by enterprise caching (but that’s another story).

    • Hi,

      using the users remote IP is a bad idea because many users sit behind a NAT-Gateway.

      Best regards
      Jean Marie

  4. A good reminder about the main properties to work with cookies offering good security levels.
    I think what’s interesting is what is actually saved in the cookies as this is a common mistake that beginner developers do: save directly sensitive information.

  5. I’m not usually much of a critic, but I was quite disappointed with that article. For one thing – you only mentioned the very basics of how cookies worked and left it at that, you briefly flew over encryption and you didn’t even mention anything that the programmer can do at an application level, such as signing.

    I find signing cookies a great way to secure any data you put in a cookie. In fact, authentication systems work great if you just chuck the user ID in a cookie and then sign the cookie to stop anyone tampering with it.

    For these examples I’ll be using PHP, but the ideas are pretty simple to implement in other languages. The trick is is to make an hash (I use SHA1) of all the data, along with an application-level secret key. I also add a random hash for good measure. Then encode it in base64 and save it to the cookie.


    define(SECRET, "frg354gifhfweufnv8r9rwe0fs9duvsnitjeityuw8fywjrek");
    // ...

    $user_id = $id;
    $hash = sha1(rand(0,500).microtime().SECRET);
    $signature = sha1(SECRET . $hash . $user_id);

    $cookie = base64_encode($signature . "-" . $hash . "-" . $user_id);
    setcookie('authentication', $cookie);

    To then get data from the cookie you simply decode it, split it by the “-” character, check the hash and return the data.


    $data = explode('-', base64_decode($_COOKIE['authentication']));

    if ($data[0] !== sha1(SECRET . $data[1] . $data[2])) {
    die('You tampered with your cookie!');
    } else {
    //All good! Do whatever here...
    }

    I hope this has helped – I liked the premise of this article but it didn’t take long to go into a bit more detail about how you can do something like this.

    Jamie

    • “I was quite disappointed with that article. For one thing – you only mentioned the very basics of how cookies worked and left it at that”

      Agree. This is old school magazine style writing to get you to buy the next issue and keep coming back. In this case, it will be “a few weeks” to get more information, and not necessarily all of it.

      It’s very irritating. The article is fragmented making it mostly useless until complete. Carson, let me know when it is complete. Then I’ll read the whole thing. Unless I Google this and find the answers on other sites.

      • My point wasn’t to try to get people to come back and read more later, its just that the cookie itself is a small part of the session. The next article focused on sessions, and uses a lot of example code, so by the time it was done it seemed big enough to stand on its own (its much longer than this post, for instance).

        I definitely understand what you’re saying though, and will keep it in mind for future posts (I am still rather new at this).

    • This technique has merit, but there are a few things to consider-

      * If you’re primarily using the data server side, it would be better to simply store it in a session. Otherwise, while you’re protected from tampering you’re still open to sniffing of sensitive data and hijacking of cookies.

      * Tying it to the user id only helps so much, as the user id won’t be present until the session is already loaded. This would, from the perspective of the hackers, just make the hash another session id. If they can steal the session (and take on the user ID) they’ll be able to steal the other cookies as well.

      * If someone logs out all of their cookies are invalidated- this may be intentional, but if you want data tied to a session it should be in the session.

      * If you’re using the data client side (javascript) you’re only going to have read only access.

      That being said, I really should have touched on this subject, as there are definite cases where it would be useful. Thank you for fixing that discrepancy on my part.

  6. You shouldn’t store sensitive information in cookies. Store the bare minimum, and implement tampering detection like kari or jamie mentions.
    Alternatively use an ID and a checksum and store it in a database… E.g.

    // To “encode”
    $id = 232; // ID to a database record, for instance
    $my_secret = md5( “Some secret, eh?” );
    $checksum = md5( md5( $id ) . $my_secret );

    // To decode:
    list( $id, $checksum ) = explode( “-“, $_COOKIE[‘the_cookie’] );
    $my_secret = md5( “Some secret, eh?” );
    if ( md5( md5( $id ) . $my_secret ) != $checksum )
    {
    die( “Tampering detected” );
    }

    In the database you can store whatever you want.

    • Lars, your method is vulnerable to session hijacking.
      See: Session hijacking on Wikipedia

  7. Pingback: links for 2009-08-24 .:: [aka щямукюшт] Ozver.in | Озверин

  8. Pingback: How to Create Totally Secure Cookies | Benzing Technologies | Creative Web Design, Affordable Web Hosting, SEO, Social Media Marketing

  9. Pingback: Daily Links for Tuesday, August 25th, 2009

  10. Pingback: vivanno.com::aggregator » Archive » Sécuriser les cookies

  11. Pingback: Ennuyer.net » Blog Archive » Rails Reading - August 26, 2009

  12. Pingback: Sécuriser les cookies | traffic-internet.net

  13. Pingback: Ansermot.ch » Secure cookies in PHP

  14. Pingback: Friday Links: 28 August 2009

  15. Pingback: Carsonified » How to Create Bulletproof Sessions

  16. Pingback: Revue de presse | Simple Entrepreneur

  17. Pingback: Mes favoris du 2-09-09 au 3-09-09

  18. Pingback: Friday Faves « dalewatkins.com

  19. Pingback: How to Create Totally Secure Cookies | Benzing Technologies

  20. In your example you used isset($_SERVER[“HTTPS”]) to ensure that cookies will only be sent when it is over an HTTPS connection. This will however not work as the this will just except the current setting. Let me explain. If I access the page via HTTP the value for this expression would be false, telling the browser to send cookies over HTTP and when accessed via HTTPS the expression would be true, thus sending cookies over the secure connection.
    For HTTP the expression
    setcookie( ‘UserName’, ‘Bob’, 0, ‘/forums’, ‘www.example.com’, isset($_SERVER[“HTTPS”]), true);
    would result to
    setcookie( ‘UserName’, ‘Bob’, 0, ‘/forums’, ‘www.example.com’,false, true);
    and for HTTPS
    setcookie( ‘UserName’, ‘Bob’, 0, ‘/forums’, ‘www.example.com’,true, true);

  21. Pingback: Internet Security: Things They Know, We Don't Know They Know!

  22. Is this line correct? >>

    session_set_cookie_params(‘o, /forums’, ‘www.example.com’, isset($_SERVER[“HTTPS”]), true)

  23. Good article. You might consider getting more in-depth on the server side aspect of sessions. Like how to store session data outside memory so you can run in a load balanced environment. Also, please consider not floating the social media toolbar at the top of the window the whole time. A bit annoying.