【Rowan Merewood】SameSite cookies explained

Protect your site by learning how to explicitly flag cross-site cookies.

Summary of Cookies

Cookies are one of the methods you can use to add persistent state to a Web site. Over the years, their capabilities have evolved and grown, but they have left the platform with some problems. To address this, browsers (including Chrome, Firefox, and Edge) are changing their behavior to enforce more privacy-preserving defaults.

Each cookie is a key=value key-value pair, along with properties that control when and where the cookie is used. You may have used these properties to set things like expiration times, or to indicate that cookies should only be sent over HTTPS. The server sets the Cookie by sending the set-cookie header in the response. For more information, you can delve into RFC6265bis. Here’s just a quick primer.

Suppose you have a blog where you want to show users the “What’s New” promotion. Users can revoke the promotion and lose sight of it for a period of time. You can store this preference in a cookie, set it to expire in one month (2,600,000 seconds), and then send it over HTTPS only. The header looks like this:

Set-Cookie: promo_shown=1; Max-Age=2600000; Secure
Copy the code

When your readers view a page that meets these requirements, that is, they are on a secure connection and the cookie is less than a month old, their browser will send this header in the request:

Cookie: promo_shown=1
Copy the code

You can also use document.cookie to add and read cookies in JS that are available for that site. Assigning document.cookie creates or overwrites cookies with the secret key. For example, you can try the following in the BROWSER’s JS console:

> document.cookie = "promo_shown=1; Max-Age=2600000; Secure"
< "promo_shown=1; Max-Age=2600000; Secure"
Copy the code

Reading document.cookie prints all cookies accessible in the current context, each cookie separated by a semicolon:

> document.cookie;
< "promo_shown=1; color_theme=peachpuff; sidebar_loc=left"
Copy the code

If you try this on some popular sites, you’ll find that most of them have set far more than three cookies. In most cases, these cookies are sent to the field on each individual request, which has many implications. For your users, the upload bandwidth is generally more limited than the download bandwidth, so the overhead of all outbound requests adds to your first byte arrival delay. Conservatively set the number and size of your cookies. Using the max-age attribute helps ensure that cookies do not linger longer than required.

What are first-party and third-party cookies?

If you go back to the site you chose in the previous section, you might notice cookies for a variety of domains, not just the cookies you’re currently accessing. Cookies that match the domain of the current web site, as shown in the browser’s address bar, are called first-party cookies. Cookies from other domains on the current site are called third-party cookies. This is not an absolute label, but relative to the user’s context. The same cookie can be either first-party or third-party, depending on the site the user is on at the time.

Continuing the example above, suppose one of your blog posts contains an image of a particularly amazing cat and hosts it at /blog/img/ kaimen-cat.png. Because it’s a nice image, there’s another person using it directly on their website. If a visitor has visited your blog and has the promo_shown cookie, the cookie will be sent in that image request when they view wondering-cat.png on someone else’s site. This is not very useful to anyone, because promo_shown is not used for anything on that person’s website, it just adds overhead to the request.

If this is an unintended effect, then why do it? Because through this mechanism, the site can remain state while being used in a third-party context. For example, if you embed a YouTube video on your site, visitors will see a “watch it later” option in the player. If your visitors are already logged in to YouTube, that session will be provided in the embedded player via a third-party cookie — meaning the “Watch Later” button will save the video once and for all, rather than prompting them to log in or have to leave your page and return to YouTube.

One of the cultural attributes of the Web is that it is open by default. This is one of the reasons so many people create their own content and applications on the Web. However, this also raises many security and privacy issues. Cross-site request forgery (CSRF) attacks rely on attaching cookies to a request from a given source, regardless of who initiated the request. For example, if you visit Evil. example, it can issue a request to your-blog.example, and your browser attaches the associated cookie. If your blog doesn’t care how to validate these requests, evil.example might trigger actions like deleting posts or adding its own content.

Users are also becoming more aware of how cookies track their activity across multiple sites. However, until now, there has been no way to express your intentions about cookies unequivocally. Your promo_shown cookie should only be sent in the first-party context, whereas window session cookies that are intentionally embedded in other sites are intended to provide login status in the third-party context.

Use the SameSite property to explicitly declare the use of cookies

The SameSite attribute (defined in RFC6265bis) allows you to declare whether cookies should be limited to first-party or same-site contexts. It is helpful to know exactly what “site” means here. The site is a combination of the domain suffix and the domain before the suffix. For example, the www.web.dev domain is part of the web-dev site.


Key terms: same-site

If the user requests the image at 'www.web.dev' and from 'static.web.dev', it is a same-site requestCopy the code

The public suffix list defines this, so it’s not just top-level domains like.com, but also services like github. IO. Your-project.github. IC and my-project.github. IO count as separate sites.


Key term: cross-site

If the user is on 'your-project.github. IO' and requests an image from 'my-project.githubCopy the code

Introducing the SameSite attribute on cookies provides three different ways to control this behavior. You can choose not to specify the property, or you can use Strict or Lax to restrict the cookie to same-site requests.

If SameSite is set to Strict, cookies are only sent in the first-party context. In user terms, a Cookie is only sent if its site matches the site currently displayed in the browser’s URL bar. Therefore, if the promo_shown cookie is set as follows:

Set-Cookie: promo_shown=1; SameSite=Strict
Copy the code

The cookie will be sent with the expected request when the user is on your site. However, when you connect to a link to your site, such as an email sent from another site or through a friend, cookies will not be sent in that initial request. When you have function-related cookies that are always after the initial navigation (such as changing passwords or buying goods), this is too strict for promo_shown. If your readers visit a link to that site, they want to send cookies so they can apply their preferences.

This is where SameSite=Lax comes in, allowing cookies to be sent through these top-level navigations. Let’s revisit the example of the cat article from above, where another site quotes your content. They use pictures of cats directly and provide links to your original article.

<p>Look at this amazing cat! </p> <img src="https://blog.example/blog/img/amazing-cat.png" /> <p>Read the <a href="https://blog.example/blog/cat.html">article</a>.</p>Copy the code

Cookies have been set as follows:

Set-Cookie: promo_shown=1; SameSite=Lax
Copy the code

If the browser requests amaing-cat.png when the reader is on someone else’s blog, the cookie will not be sent. But when a reader accesses cat.html on your blog via a link, the request will contain a cookie. This makes Lax a good choice for influencing cookies displayed on your site, and Strict is useful for cookies related to actions performed by your users.


Pay attention to

Neither 'Strict' nor 'Lax' is a complete solution to site security. Cookies are sent as part of a user request, and you should treat them the same as any other user input. This means sanitizing and validating inputs. Do not use cookies to store data that you consider confidential on the server side.Copy the code

Finally, you can choose not to specify a value that previously implicitly declared how you wanted cookies to be sent in all contexts. The latest draft of RFC6265bis makes this clear by introducing a new value of SameSite=None. This means that you can use None to explicitly indicate that you intend to send cookies in a third-party context.


If you use services used by other sites, such as widgets, embedded content, membership programs, advertising, or logins across multiple sites, use 'None' to ensure that your intent is clearCopy the code

Change the default behavior without SameSite

Although the SameSite attribute is widely supported, it has unfortunately not yet been widely adopted by developers. Open sending cookies by default means that all use cases are available, but users are vulnerable to CSRF and accidental information leakage. To encourage developers to state their intent not to provide a more secure experience for users, the IETF proposal “Progressive Better Cookies” provides two key changes:

  • There is noSameSiteCookies of the property are considered to beSameSite=Lax
  • SameSite=NoneCookie must also be specifiedSecureWhich means they need a secure context

Chrome has implemented these behaviors since version 80. Firefox has been testing them since Firefox 69 and will make them default behavior in the future. To test these behaviors in Firefox, open the abount: config and set up the network. The cookie. SameSite. LaxByDefault. Edge also plans to change its default behavior.


This article will be updated when other browsers announce support.

The default SameSite = Lax

Unset properties:

Set-Cookie: promo_shown=1
Copy the code

If the cookie is sent without setting any SameSite properties

Default behavior:

Set-Cookie: promo_shown=1; SameSite=Lax
Copy the code

The browser will assume that the cookie is set to SameSite=Lax

While this is to apply more secure defaults, ideally you should set an explicit SameSite property and not rely on the browser to apply it for you. This makes your intent on cookies clear and improves the chances of a consistent experience across browsers.


Pay attention to

The default behavior applied by Chrome is a little looser than the default SameSite=Lax because it will allow certain cookies to be sent on top-level POST requests. You can see the exact details in the Blink-dev bulletin. This is just a temporary link error, you should still fix cross-site Cookies to use SameSite=None; Secure

SameSite=None must be secure

Setting cookies without Secure will be rejected:

Set-Cookie: widget_session=abc123; SameSite=None
Copy the code

You must make sure to pair SameSite=None with the Secure property:

Set-Cookie: widget_session=abc123; SameSite=None; Secure
Copy the code

To test this behavior, enable Chrome ://flags/#cookies-without-same-site-must-be-secure in Chrome 76 and set about:config: in Firefox 69. Network. Cookies. SameSite. NoneRequiresSecure you will need to set a new cookie used this feature, and take the initiative to refresh the existing cookies, even if these cookies is not valid.


If you rely on any service that provides third-party content on your site, you should also ask the provider if they are updating their service. You may need to update dependencies or code snippets to ensure that your site adopts the new behavior.Copy the code

Both of these changes implemented the previous version of the SameSite property correctly or did not support its browser backward compatibility at all. By applying these changes to cookies, you can explicitly specify their intended use, rather than relying on the browser’s default behavior. Also, any client that does not recognize SameSite=None should ignore it and proceed as if the property had not been set.


warning

Many older browsers (including Chrome, Safari, and UC browsers) are incompatible with the new None property and may ignore or restrict cookies. This behavior is fixed in the current release, but you should check the traffic to determine the percentage of users affected. You can check out the list of known incompatible clients on the Chromium website.

SameSite cookie recipes

For more information on accurately updating cookies to successfully handle these changes to SameSite=None and differences in browser behavior, go to the followup article: SameSite Cookir Recipes