The term cookie comes from the term magic cookie, which is a packet of data a program receives and sends back unchanged
Cookies were invented in 1994 by Netscape, when developing an e-commerce application. They didn’t want to store partial transaction states on the server side and instead wanted to move them to the client side. Cookies were born as a virtual shopping cart
There were groups formed to discuss the security considerations of cookies in 1997 but they were ignored by Netscape and Internet Explorer (drama). We have our final Cookie RFC in 2011.
Cookie Definition
A cookie is a small block of data created by a web server and placed on the user’s device by the web browser
Cookie Types
session cookie
persistent cookie
secure cookie: can only be transmitted via https
http-only cookie: can not be accessed by client-side apis
same-site cookie: Strict | Lax | None
browser only sends cookie to target domain with same origin
chrome/firefox/edge support this but for cookies without the SameSite attribute chrome treats them as None which ruins it
zombie cookie: cookie information hidden outside cookie storage1
eg could be in flash, web history, web cache, localStorage, sessionStorage, indexedDB etc
restored as normal cookie by some js whenever its found missing from the cookie storage
Implementation and Structure
A cookie is just a name:value pair with some attributes
/* example structure */struct cookie_t { string name; string value; struct attributes_t* attr;};struct attributes_t { /* set by server */ string domain; string path; bool secure_only; bool https_only; /* stored by user agent */ bool persistent; bool host_only; time_t last_access_time; time_t creation_time; time_t expiry_time;}
(it can have 0 or more attributes, but a user-agent (browser) stores all the above attributes, setting defaults where required)
Cookies are set using the response headerSet-Cookie:
What domain is associated when run as file:///? (why do we need SameSite=None?)
No domain is associated in firefox (path became /home/<user>/ for me)
I don't think we actually need SameSite=None, its just that some browsers require a SameSite policy when giving it Secure
Domain and Path define the scope of the cookie, they tell the browser what website the cookie belongs to. The Domain attribute specifies the hosts that to which the cookie will be sent. The Path attribute adds further isolation, the browser sends these cookies only on the requests to subpaths of the attribute. The browser also sends the cookies to the subdomains that match
If the server doesn’t specify them they default to the domain and the path of the resource that was requested. Most browsers will set the host_only attribute for these cookies and NOT include subdomains.
Can the Server set any Domain?
Since the server provides the domain when setting a cookie, it could potentially set a domain other than itself? [attacker.com] could set a client cookie for [google.com] that would be sent to google on every request.
This is undesirable and the browser checks that the server only sets domains that belong to it (the domain or specific subdomains)
What if the cookie was set by a script? (What domain is associated with the script?)
defaults to domain of wherever script was run, could potentially be "", if its something random it just won’t be set
The LSID cookie can only be sent to [domain.com/accounts/*] whereas HSID and SSID can be sent to [*.domain.com/*] (all subdomains and paths)
Expires and Max-Age
Expires defines a specific date and time for when the browser should delete a cookie. Max-Age provides a relative offset in seconds however its uncommon and some browsers (*cough* internet explorer *cough*) don’t support it. If supported though you should be using Max-Age
The format is Wdy, DD Mon YYYY HH:MM::SS GMT
Expires=Tue, 11 Apr 2017 06:32:24 GMT
If this field isn’t provided the browser sets it as a session cookie and deletes it when the connection is closed
Secure/HttpOnly/SameSite
Secure tells the browser to encrypt the cookie and only send it over HTTPS (this doesn’t stop it from being sent over HTTP lmao, make sure to set it from HTTPS too)
always have the Secure attribute
HttpOnly tells the browser not to expose cookies through other channels. This prevents client-side JS from stealing your cookie and mitigates XSS
always have this on for session ids
can’t set an HttpOnly cookie from client-side script
SameSite isn’t part of the RFC but implemented by browsers. only send coookie in same-site contexts. Mitigates CSRF
blocks sending cookies in requests from other websites
img fetches from other websites wont have your cookies
redirects from another website won’t include your auth token so you’ll have to login again
can be Strict, Lax, or None
Cookie Theft And Session Hijacking
Most websites use cookies as the only identifiers for user sessions. Which means if you get someone’s cookies you can impoersonate someone else
Packet Sniffing
From a network attacker model, since cookies are sent over the network to the server with every request, if they are not encrypted, anyone listening over the network could steal them. This is why we use the Secure attribute on cookies, to ensure they are not sent over unencrypted channels.
DNS Cache Poisoning
Another network attack is if an attacker manages to modify a DNS server. The attacker creates a record for
www.example.com. 299 IN A 104.18.27.120attacker.www.example.com. 299 IN A 69.69.420.67
Now if any request goes to attacker.www.example.com it also contains all of the user’s cookies for www.example.com.
However if the cookie is Secure the attacker still has to take the trouble of obtaining a certificate for attacker.www.example.com to prevent the browser’s certificate mismatch warning. For the attacker to do so legitimately example.com must not have HSTS (strict transport security) for the root and its subdomains, and the cookies must not be host_only
Cross Site Scripting
If the website takes unsanitized user input somewhere, you have xss
document.cookie
returns all the cookies for the current domain. If you can inject this html/js on a website, you can fetch the cookies and do with them what you want.
Anytime Bob visits attacker.com containing the above image tag, a request to bank.example.com is made on his behalf, and the browser attaches his cookies with it. The bank receives an authorised request and Bob loses his money