Cookies are metadata used to keep track of HTTP sessions. This is because HTTP is a stateless protocol — each HTTP session is independent of another.
A website sets a cookie in your browser to recognize you. This allows you to continue your browsing activity from where you left off (online shopping for example) because your browser includes that cookie every time you make subsequent visits to the website in the future.
According to RFC 6265, cookies are included in either the Cookie or Set-Cookie header fields. Since a website sets cookies into the browser of the client, the Set-Cookie header is seen in an HTTP Response from the server. When the client returns to the website in the future, it will include these cookies in the Cookie header within the HTTP Request. The Cookie header sent by the client contains cookies that were set previously by the webserver in the Set-Cookie header.
We can observe the setting of cookie headers by sending an HTTP Request to a website we not visited recently. Let’s go to www.cnn.com and intercept it with Burp Suite.
There are no Cookie headers sent by my browser because this is not a website I have visited at all. It’s the first time I am visiting this webserver.
Zoom image will be displayed
The webserver at responds. In the HTTP Response, we can see that the HTTP Server at www.cnn.com sets a few cookies in the Set-Cookie headers as seen below:
The server has set 4 cookies: SecGpc, countryCode, stateCode, geoData
Set-Cookie: SecGpc=0; Domain=.cnn.com; Path=/; SameSite=None; Secure
Set-Cookie: countryCode=SG; Domain=.cnn.com; Path=/; SameSite=None; Secure
Set-Cookie: stateCode=04; Domain=.cnn.com; Path=/; SameSite=None; Secure
Set-Cookie: geoData=singapore|04|423466|SG|AS|800|cable|1.320|103.920|-1; Domain=.cnn.com; Path=/; SameSite=None; SecureThese cookies in particular are designed to store the client’s geolocation information. This information is useful for cnn.com to customize content based on the region I am visiting from, among other reasons such as regional based compliance (that’s one rabbit hole I will pursue on another day).
All four of these cookies have the attribute: Domain=.cnn.com. These cookies are scoped to .cnn.com which means that my browser will include these cookies in HTTP requests made to all cnn.com subdomains such as edition.cnn.com, www.cnn.com, etc.
In this case, we can see the HTTP Response is redirecting us with a 302 Response Code to another subdomain which is specified in the location header: edition.cnn.com
HTTP/2 302 Found
..
..
..
Location: https://edition.cnn.com/Although we are being redirected to another location at https://edition.cnn.com/ we should still expect to see these cookies in subsequent HTTP Requests made from my user-agent to the server because of what I mentioned previously about the scope, so let’s have a look at what the next request looks like.
In the very next GET request to edition.cnn.com, we can see a Cookie header which includes all four cookies (SecGpc, countryCode, stateCode, geoData) previously sent in the Set-Cookie header by the web server.
Zoom image will be displayed
GET / HTTP/1.1
Host: edition.cnn.com
Cookie: SecGpc=0; countryCode=SG; stateCode=04; geoData=singapore|04|423466|SG|AS|800|cable|1.320|103.920|-1
..
..
..According to RFC 6265 these cookies will expire at the end of the session since there were no Max-Age or Expires attributes set in the Set-Cookie header by the website. These are also known as session cookies. They expire when the browser closes.
RFC 6256: Unless the cookie’s attributes indicate otherwise, the cookie is returned only to the origin server (and not, for example, to any subdomains), and it expires at the end of the current session (as defined by the user agent). User agents ignore unrecognized cookie attributes (but not the entire cookie).
RFC 6265 section 4.1.2: When the user agent receives a Set-Cookie header, the user agent stores the cookie together with its attributes.
Cookie attributes are additional pieces of information that control the behavior of cookies. They can determine when a cookie expires, or if there should be a security measure associated with them.
The following attributes dictate the lifespan of a cookie:
Expires: Represents maximum lifetime of a cookie as the expiration date and time.Max-Age: Represents maximum lifetime of a cookie in number of seconds.In both cases, the user-agent is allowed to evict the cookies before the expiry. If a cookie contains both these attributes, the Max-Age attribute has precedence and controls the expiration date of the cookie.
Other attributes include (but are not limited to) the following:
Domain: Specifies hosts to which the user-agent will send the cookie. If google.com sets a Domain attribute to a cookie, then the client’s user-agent will send that cookie in HTTP Requests to google.com, www.google.com, and www.example.google.com.Path: The scope of a cookie is limited to a specific path/URI such as /resultsSecure: The user-agent will only include this cookie in an HTTP Request over HTTPS.HttpOnly: Limits the scope of the cookie to HTTP only. This prevents scripts (such as Javascript) from accessing the cookie.There are several cookies that are intended to mitigate web application security risks. According to OWASP, the HttpOnly cookie helps mitigate the risk of client side script accessing the protected cookie. Browsers that support the HttpOnly flag will prevent revealing the cookie to a third party.
Cross site scripting (XSS) attacks target cookies that may contain session identifiers. An attacker who can access the cookie, can steal it and replay it to impersonate the victim. This is also known as Session Hijacking.
How would a browser prevent a third party from accessing a cookie with the HttpOnly flag enabled? I want to test this by configuring my Wordpress website to set a test cookie with the HttpOnly flag. I have added the following code into the functions.php file of my current Wordpress Theme. It will instruct my Wordpress site to set a test cookie.
test_cookiesecret_valueadd_action('init', function() {
setcookie('test_cookie', 'secret_value', [
'expires' => time() + 3600,
'path' => '/',
'secure' => is_ssl(),
'httponly' => true, # We are testing the HttpOnly flag
'samesite' => 'Lax'
]);
});Now when I visit my site, the server sets the test cookie in the HTTP Response. We can view this in Google Chrome’s developer tools under Network -> Cookies. HttpOnly is checked.
Zoom image will be displayed
Next, I try running this command in the console: document.cookie and I am unable to see any information. There is no output. That is because my browser doesn’t allow me to read the test cookie and its value that was stored earlier because of its HttpOnly attribute.
The
document.cookieproperty in web browsers allows client-side JavaScript to read and write cookies associated with the current document. However, cookies set with theHttpOnlyflag are a specific exception to this rule.
What if I create a new test cookie named: new_test_cookie without the HttpOnly flag by modifying the earlier code? I will toggle HttpOnly to ‘false’ this time:
new_test_cookiesecret_valueadd_action('init', function() {
setcookie('new_test_cookie', 'secret_value', [
'expires' => time() + 3600,
'path' => '/',
'secure' => is_ssl(),
'httponly' => false, # Toggling httponly to false
'samesite' => 'Lax'
]);
});If we reload the page again, we see 2 cookies now. One is set without the HttpOnly flag:
Zoom image will be displayed
This time, when I run document.cookie into my console, I am able to see the newly created cookie’s name and value. This is because this cookie was not set with the HttpOnly flag.
The original test_cookie with the HttpOnly flag enabled is still not revealed. Similarly, other attempts to read or modify HttpOnly cookies using Javascript will fail. This is a simple way to explain how a browser can protect a victim from having their session ID (SID) stolen by an attacker, because of the webserver opting to attribute the HttpOnly flag to a sensitive cookie.
Should all cookies include the HttpOnly attribute? In order to prevent sensitive data leakage, cookies such as session identifiers and authentication tokens should have this flag enabled.
On the contrary, some cookies when set are not attributed with the HttpOnly flag. One example is bot defense cookies inserted by reverse proxies such as F5. These reverse proxies will use JavaScript challenges to determine if the connecting client is a human or bot.
The JavaScript challenge runs in the browser and collects telemetry such as mouse movements. It then sets a cookie using JavaScript, typically with document.cookie, to store the telemetry token for validation on future HTTP requests.
Since JS needs to read/write the cookie during telemetry collection, it cannot be set with the HttpOnly attribute.