gingerbread man lock

Secure the Cookie!

Cookies are a method by which a web server can convince a web client to remember, and return, some information. It’s technically a violation of the web since the HTTP protocol is intended to be idempotent. Some also consider it a violation of privacy.

However, in this video we are not going to take a position on the good/bad/ugly of cookies. Instead we are going to focus on how to use them securely.

First, let’s understand how long it will live. Cookies have a `expires` and `max-age` attribute. If you set either of these (to some time in the future), the cookie will survive a browser restart (a persistent cookie). If you don’t set it, it becomes a session cookie.

So, we’ve just learned our first lesson: the more sensitive the data, the shorter the expiry should be to limit the damage.

Second, let’s learn about the `secure` flag. How great is it to make security be a Boolean! Well, it’s not that great here. The Secure flag means ‘do not send unencrypted’ (in practise, only send over HTTPS). So we’ve learned our second lesson: always use Secure. No excuses!

Third, let’s talk about the httpOnly flag. Well this is a weird one, its all HTTP right? Well, it turns out they mean more HTML-only. If httpOnly is set, we cannot read the cookie from JavaScript. You want this, it prevents Cross-Site-Scripting (XSS) type attacks where rogue JavaScript snoops around. If you have JavaScript that needs to read cookies, maybe try and solve that. Now, the httpOnly flag is not an absolute, there is a type of attack called Cross-Site-Tracing (XST). Purists will tell you to disable the TRACE method on your web server.

Not content w/ httpOnly and Secure, the cookie committee created the SameSite flag.  Sometimes called First-Party cookies it allows a server to reduce the impact of a Cross-Site Request Forgery (CSRF) attack by forcing a cookie to only be sent with requests from the same domain. Not universally implemented, somewhat newer, support is still strong. Do your best to set this to ‘strict’. If you find that your POST fail, and you cannot fix your application, have a long discussion with yourself about `lax`.

OK, that must be it, right. Wait, there’s more? The HostOnly flag specifies if a cookie can be used on subdomains. If you are doing single-sign-on across multiple sites, you want this set to the domain. For individual web applications, you likely want the domain field blank. A cookie with a domain attribute controls where it will be sent.

Seem complex? Just do this:

  • Don’t use cookies unless you must
  • Don’t set expiry, or use a very short one
  • Use httpOnly always
  • Use Secure always
  • Use SameSite strict unless you can’t
  • Leave domain empty unless used for single sign on

If you want to see an example of how you could use Lua OpenResty, a Web Application Firewall, to add some after-the-fact-security, have a look below!

local headers = ngx.req.get_headers()
if headers["x-forwarded-proto"] == "https" then
  local ck = require "resty.cookie"
  local cookie, err = ck:new()
  local fields, err = cookie:get_all()
  if fields then
    for k, v in pairs(fields) do 
      local ok, err = cookie:set({ 
        key = k, 
        value = v, 
        path = "/", 
        httponly = true, 
        secure = true, 
        samesite = "Strict" })