Cookies are small packets of data which a server can send to your browser to store some configuration or personal data. The browser automatically sends them along with each request to that same server. The contents are usually very interesting to hackers, so it’s important to know how to secure these cookies. Fortunately there are a lot of things you can do to improve cookie security. So… what do you need to know?
What data do you store?
If you want to store sensitive data, think very hard if you really need to store that particular bit of data in a cookie. By using cookies, you may prevent expensive requests to the server, but the data may also get outdated. Data is always more secure if it not stored on the client side.
Assuming you decided that you really do need cookies, you need to make sure that you configure them correctly. Cookies have several attributes and flags to do so. Below are the ones you need to know about when considering cookie security.
Session Cookie vs. Persistent Cookie
First of all, decide how long your cookie should be valid. The more sensitive the data, the sooner it should expire. Cookies allow you to specify this through the ‘expires’ and ‘max-age’ attributes. By definition, setting either of these attributes make the cookie persistent. This means that (as long as the expiration is in the future), the cookies survive a browser restart. If both fields are omitted, you get a non-persistent cookie or session-cookie. This means the cookie is automatically removed when your session ends (so when the browser is closed).
- The more sensitive the data, the sooner you’ll want the cookie to expire, so if you explicitly want to set an expiration or max-age, choose a date in the next few months, weeks or even hours, rather than years.
If the browser sends cookies over unencrypted connections, it will be possible for hackers to eavesdrop on your connection and read (or even change) the contents of your cookies. To prevent this, send cookies over encrypted connections only.
Setting the secure flag prevents the cookie from ever being sent over an unencrypted connection. It basically tells the browser to never add the cookie to any request to the server that does not use an encrypted channel. The cookie will only be added to connections such as HTTPS (HTTP over Transport Layer Security (TLS)). Note that it is up to the browser to decide what it considers ‘secure’. Typically the browser considers it secure if the protocol makes use of the secure-transport-layer. This also means that a browser may decide to send the cookie when the connection is secured with a self-signed or expired certificate.
- You should always set the Secure flag in your cookies when they contain sensitive data, unless your website uses an insecure connection, but in that case you have much bigger problems.
You might think that setting this cookie is not relevant if your server always uses HTTPS, but that is not true. It means that the server would never send unencrypted data (including cookies) to the browser, but the other direction is not guaranteed. E.g. a network attacker could intercept outbound HTTP requests and redirect them to capture the plaintext cookies.
Even if the server uses HTTP Strict Transport Security (HSTS) and includes subdomains and the domain is on the preload list, it’s a good practice to still set the Secure Flag. Not all browsers and user agents use the preload list, so an initial request to your domain could still use an unencrypted channel.
The HTTPOnly flag prevents scripts to read the cookie. As the name HTTPOnly implies, the browser will only use the cookie in HTTP requests. This prevents hackers from using XSS vulnerabilities to learn the contents of the cookie. E.g. for the sessionId cookie it is never necessary to read the cookie with client-side script, so for sessionId cookies, you can always set the HTTPOnly flag.
- Set the HTTPOnly flag for all cookies that don’t need to be accessed by script.
It’s good to know that for a hacker there are other techniques to learn the contents of the cookie. Even if the HTTPOnly flag is set, you can use script to learn the contents. Ever heard of the HTTP TRACE method? It is a method (like GET and POST) that is intended for debugging. When using the TRACE method in a request, the server just echoes the exact contents of the request back to you (including cookies), so you can see what your browser sent. This is great for debugging! It can however also be used by malicious scripts, because even though your script cannot read the cookie directly, it can read the response of a TRACE request. This is called Cross-Site Tracing (XST).
- Besides setting the HTTPOnly flag, you should always disable the TRACE method on any non-Development server.
Another thing to keep in mind is that there are other tools that echo HTTP requests. E.g, there are Docker containers that echo the HTTP request, to help you debug your microservices. While very useful in development environments, such services should never end up in Production.
The SameSite flag is an experimental flag, which Google added in Chrome 51. It aims to mitigate the risk of CSRF. When the server sets its value to ‘strict’, the browser will not send the cookie to your website if the request comes from a different domain, even when directly clicking a link. For example, a bank doesn’t want financial transactions to be initiated through a link on a different domain. There it makes sense. Facebook on the hand practically lives from letting users click ‘like’ buttons on different domains, so they won’t use this. Setting the SameSite flag to the value ‘lax’ makes the browser a bit more lenient in that it only blocks the cookies with ‘unsafe’ HTTP methods like ‘POST’.
- Set SameSite to ‘strict’ if linking from other sites is not necessary. Set it to ‘lax’ otherwise.
The HostOnly flag specifies whether the cookie is accessible by subdomains or not. It is an implicit flag that the browser sets if the domain attribute is empty. E.g. If the website http://www.example.com sets a cookie without a Domain attribute, it is a HostOnly cookie. Subdomains like foo.example.com will be able to read it. If http://www.example.com sets a cookie with Domain=example.com, then it is no longer a HostOnly cookie, and all subdomains of example.com will have access to its contents.
- Leave the Domain attribute empty, unless you explicitly want to share the contents of the cookie with all subdomains and know it’s safe to share the contents with all of them.
So, to summarize:
- Don’t store sensitive data in cookies unless you absolutely have to.
- Use Session cookies if possible. Otherwise set a strict expiration.
- Use the HttpOnly and the Secure flags of cookies.
- Set the SameSite flag to avoid other websites to link to your site.
- Explicitly set a domain to avoid subdomains to use the cookie.
After that, your cookie data should be much safer.
Here you can find more information on the cookie specification: https://tools.ietf.org/html/rfc6265