SameSite Cookies and Why You Need Them
Tags: security, webdev, cybersecurity, programming
Published: 2021-02-25 23:52:44

The original post can be found [here](https://www.appsecmonkey.com/blog/samesite-cookies/).

## What are SameSite cookies?
[SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) is a cookie security attribute [introduced in 2016](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00). Its purpose is to prevent cookies from getting included in cross-site requests in order to mitigate different client-side attacks such as [CSRF](https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/), [XS-Leaks](https://www.appsecmonkey.com/blog/xs-leaks/) and [XSS](https://www.appsecmonkey.com/blog/xss-attack-and-prevention/).

## What is a cross-site request?
It's a request from another website.

For example, when evil.com loads an image from example.com, it's a cross-site request. It's also a cross-site request when evil.com frames example.com or navigates to it.

So what's a site? To be specific, the `site` of an URL is calculated from the `host` part by taking the *effective* [TLD](https://developer.mozilla.org/en-US/docs/Glossary/TLD) (I'll get to the effective thing in a minute) and the part immediately before it. This combination is called `eTLD+1`.

So the `host` part of *"https://www.appsecmonkey.com"* is *www.appsecmonkey.com*.

And the effective TLD is *com*. And the part immediately before the TLD is *appsecmonkey*. So the site is *appsecmonkey.com*.

![eTLD + 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1nmocs0x1hcdpc9wmej1.jpg)

Using the same formula, the site for *http://a.b.c.appsecmonkey.com:8080/* is also `appsecmonkey.com`.

![eTLD + 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ey0qo29dmnvnk7msuaq.jpg)

Note that unlike [cross-origin requests](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy), the `scheme` (http) and `port` (8080) are *not* relevant in cross-*site*-requests.

Finally, I promised to clarify the effective TLD thing. The TLD of *https://www.appsecmonkey.co.uk* is *.uk*. But the effective TLD, or eTLD, is *.co.uk*. The list of these "eTLD suffixes" such as *.co.uk* is facilitated by the [public suffix list](https://publicsuffix.org/).

![eTLD + 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvh3zktapqamc8dwjbtq.jpg)

To summarize, the site is eTLD+1, and any interaction where the source and destination have a different site is called *cross-site*.

## Lax vs. Strict vs. None
SameSite cookies have three modes: `Lax`, `Strict` and `None`.

Generally, `Lax` is suitable for all applications, while `Strict` tends to be a better fit for security-critical applications. `None` is just for opting out.

### Lax
`SameSite=Lax` will protect the cookie from *cross-site* interactions in a *third-party context*. These include: 
- evil.com loading a resource (image, style, script, etc.) from example.com.
- evil.com loading example.com in an iframe.
- evil.com submitting a form to example.com.
- evil.com sending a fetch request to example.com.

Lax cookies, however, *will* be sent when navigating. For example: 
- evil.com has a link to example.com (which the user clicks).
- evil.com redirects the user to example.com.
- evil.com calls window.open('example.com').

You can use it like so.

```yaml
Set-Cookie: foo=bar; SameSite=Lax; ...
```

### Strict
`SameSite=Strict` has all the protections of the lax mode, with the addition that it also protects the cookies when navigating.

Browsers include `SameSite=Strict` cookies only in *first-party context*, which is to say when the user types something into the URL bar and presses enter (or uses a bookmark).

While the *strict* mode is the most secure, it has drawbacks such as breaking links to the application, so it's not always suitable.

```yaml
Set-Cookie: foo=bar; SameSite=Strict; ...
```

### None
`SameSite=None` opts out of the protection when you explicitly *want* to send the cookie in cross-site interactions. It is necessary because browsers [have started to](https://tools.ietf.org/html/draft-west-cookie-incrementalism-00) enable `SameSite=Lax` as the default (which is awesome).

In order to use `SameSite=None`, you [are required](https://tools.ietf.org/html/draft-west-cookie-incrementalism-00#section-3.2) to specify the `Secure` flag as well. The secure flag will prevent the cookie from leaking over an unencrypted connection. I don't fully understand the reasoning behind requiring it, but apparently, it has something to do with [pervasive monitoring](https://tools.ietf.org/html/rfc7258).

```yaml
Set-Cookie: foo=bar; SameSite=None; Secure; ...
```

At any rate, it's always a good practice to use the other cookie security features as well: [Secure](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies), [HttpOnly](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies) and [__Host-prefix](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#cookie_prefixes).

```yaml
Set-Cookie: __Host-foo=bar; SameSite=Lax; Secure; HttpOnly; Path=/
```

## SameSite vs. CSRF
One of the best features of SameSite cookies is their capability to prevent [CSRF (Cross-Site Request Forgery)](https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/) attacks.

Here is how a CSRF attack might work. Let's pretend that our user logs in to *appsecmonkey.com*, which sets the user's session cookie like so.

```yaml
Set-Cookie: SessionID=ABC123; Secure; HttpOnly
```

And a malicious website, evil.com, hosts the following HTML.

```html
<form method="POST"  action="https://www.appsecmonkey.com/user/update-email/">
  <input type="hidden" name="new_email" value="attacker@evil.com" />
</form>
<script type="text/javascript">
  document.badform.submit();
</script>
```

When the unwitting user browses to the URL, the following *cross-site* `POST` request to *appsecmonkey.com* gets sent from the user's browser: 

```yaml
POST /user/update-email/ HTTP/1.1
Host: www.appsecmonkey.com
Cookie: SessionId=ABC123
...

new_email=attacker@evil.com
```

And the user's email address gets changed to `attacker@evil.com`.

However, if *appsecmonkey.com* sets the cookie as *SameSite*, and the same attack happens, the resulting POST request will not contain the cookie.

```yaml
Set-Cookie: SessionID=ABC123; Secure; HttpOnly; SameSite=Lax
```

```yaml
POST /user/update-email/ HTTP/1.1
Host: www.appsecmonkey.com
...

new_email=attacker@evil.com
```

And the user's email remains intact.

> **Note**
> 
> CSRF synchronizer tokens are the primary defense against CSRF attacks. SameSite is merely an extra layer of security, hardening that should be used *in addition to* the actual security control (CSRF tokens) to achieve defense-in-depth.

## SameSite vs. HTTP Verb Misuse CSRF

Imagine the same scenario, but this time *appsecmonkey* has blundered even worse than forgetting CSRF tokens. This time whe web application uses HTTP `GET` for changing stuff. Namely, the user's email address.

This means that just getting the target user to *browse* to the URL `www.appsecmonkey.com/user/update-email?new_email=attacker@evil.com` would be enough to change the email.

This time `SameSite=Lax` won't help. But if the web application sets the cookie in *strict* mode, the attack will fail again.

```yaml
Set-Cookie: SessionID=ABC123; Secure; HttpOnly; SameSite=Strict
```

## SameSite vs. Reflected XSS
[XSS (Cross-Site Scripting)](https://www.appsecmonkey.com/blog/xss-attack-and-prevention/) vulnerabilities arise when untrusted data gets interpreted as code in a web context. They usually result from: 

1. Generating HTML unsafely (parameterizing without encoding correctly).
2. Allowing users to edit HTML directly (WYSIWYG editors, for example).
3. Allowing users to upload HTML/SVG files and serving those back unsafely.
4. Using JavaScript unsafely (passing untrusted data into executable functions/properties).
5. Using outdated and vulnerable JavaScript frameworks.

SameSite doesn't prevent all XSS attacks. But it can serve as a pretty good extra layer of security against *reflected* XSS, especially in `Strict` mode.

### What are reflected XSS attacks?
XSS vulnerabilities are *reflected* if malicious links or websites can exploit them. For example, look at `search.php` here.

```php
echo "<p>Search results for: " . $_GET('search') . "</p>"
```

The URL parameter `search` gets reflected as part of the returned document's HTML structure. An attacker could create a link like so, rendering malicious JavaScript on the page when someone opens the link.

```html
https://www.example.com/?search=<script>alert("XSS")</script>
```

The returned HTML looks like this.

```html
<p>Search results for: <script>alert("XSS")</script></p>
```

### How are XSS vulnerabilities exploited?
An attacker can exploit XSS vulnerabilities by injecting JavaScript code that performs unwanted actions in the logged-in user's session.

### How does SameSite help?
In the above example, SameSite in `Lax` mode wouldn't help because it doesn't protect from navigating to links. If the XSS were in a POST request, then it would help.

However, SameSite in `Strict` mode would have prevented the attack.

## SameSite vs. XS-Leaks
[XS-Leaks](https://www.appsecmonkey.com/blog/xs-leaks/) (or Cross-Site Leaks) are a [set of browser side-channel attacks](https://xsleaks.dev/). They enable malicious websites to infer data from the users of other web applications.

For example, browsers make it easy to time cross-domain requests.

```js
var start = performance.now()

fetch('https://example.com', {
  mode: 'no-cors',
  credentials: 'include'
}).then(() => {
  var time = performance.now() - start;
  console.log("The request took %d ms.", time);
});
```

```
The request took 129 ms.
```

Cross-site timing makes it possible for a malicious website to differentiate between responses. Suppose that there is a search API for patients to find their records. If the patient has diabetes and searches for "diabetes", the server returns data.

`GET /api/v1/records/search?query=diabetes`
```yaml
{"records": [{"id": 1, ...}]}
```

And if the patient doesn't have diabetes, the API returns an empty JSON.

`GET /api/v1/records/search?query=diabetes`
```yaml
{"records": []}
```

Generally, the former request would take a longer time. An attacker could then create a malicious website that clocks requests to the "diabetes" URL and determines whether or not the user has diabetes.

You can expand the attack and search for a... b... c... d... yes. da.. db... di... yes. This sort of attack is known as [XS-search](https://xsleaks.dev/docs/attacks/xs-search/).

### How does SameSite help?
In this case, `SameSite=Lax` is enough to prevent the attack, because the `fetch` requests happen in a *third party context*. It also helps prevent other xs-leaks such as [monitoring error events](https://xsleaks.dev/docs/attacks/error-events/), [frame counting](https://xsleaks.dev/docs/attacks/frame-counting/), [monitoring navigations](https://xsleaks.dev/docs/attacks/navigations/) and [monitoring blur events](https://xsleaks.dev/docs/attacks/id-attribute/).

## Summary
Using SameSite cookies will significantly improve your application's client-side security, protecting against XSS, CSRF, and XS-Leak attacks.

The strict mode has drawbacks and might not be the best fit for most applications, but luckily the lax mode covers most attacks.

The lax mode is becoming the default as I write, so [make sure you are ready for the change](https://web.dev/samesite-cookie-recipes/).

## Get the web security checklist spreadsheet!
[![Subscribe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3iqd1ef4a5a0icyvvk65.jpg)](https://eepurl.com/hqAGt5)
 ☝️ [Subscribe](https://eepurl.com/hqAGt5) to AppSec Monkey's email list, get our best content delivered straight to your inbox, and <b>get our 2021 Web Application Security Checklist Spreadsheet for FREE</b> as a welcome gift!

## Don't stop here
If you like this article, check out the other application security guides we have on [AppSec Monkey](https://www.appsecmonkey.com/) as well.

Thanks for reading.