Cookies
(This section assumes that you have a basic knowledge of what Cookies are. For proper introduction and more details about cookies, please read the Mozilla Developer Docs: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies. The flowcharts in this section are created by Nandan Desai)
Cookies are created on the client side when either the server responds with Set-Cookie
header or when the cookies are set using Document.cookie
in JavaScript.
And the browser sends the Cookies to the server on the next request using the Cookie
header.
Understanding the intricacies of cookies boils down to understanding various cookie attributes.
Cookie attributes can be classified into two main categories:
- The attributes that restrict access to cookies (like
Secure
andHttpOnly
) - The attributes that define where the cookies are sent (like
SameSite
,Domain
andPath
)
'Secure' and 'HttpOnly'
Cookies that have Secure
attribute set are only sent to the server on an encrypted (HTTPS) network. Also, sites with http://
in their URL can't set Secure
attribute on cookies.
Cookies that have HttpOnly
attribute set are not accessible via JavaScript and are sent to the server by the browser automatically when all the proper conditions are met.
'Domain', 'Path' and 'SameSite'
- 'Domain' attribute
- 'Path' attribute
- 'SameSite' attribute
Here, the "site" refers to the domain combined with the scheme (http or https). For example, http://example.com
and https://example.com
are different sites according to this definition.
CORS (Cross-Origin Resource Sharing)
(Most of the CORS-related content presented here is either a direct copy-paste of Mozilla Developer Docs or I've made some minor modifications to make certain things simpler to understand. You can get more details on this topic here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
In simple words, CORS is a communication between the server and the browser about what the browser is allowed to do when it receives some content from the server. It's like the server is doing Access Control on the browser.
Suppose web content at https://foo.example
wishes to invoke content on domain https://bar.other
. Code of this sort might be used in JavaScript deployed on foo.example
:
const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/';
xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();
This operation performs a simple exchange between the client and the server, using CORS headers to handle the privileges:
Let's look at what the browser will send to the server in this case:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
The request header of note is Origin
, which shows that the invocation is coming from https://foo.example
.
Now let's see how the server responds:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[…XML Data…]
In response, the server returns a Access-Control-Allow-Origin
header with Access-Control-Allow-Origin: *
, which means that the resource can be accessed by any origin.
This pattern of the Origin
and Access-Control-Allow-Origin
headers is the simplest use of the access control protocol. If the resource owners at https://bar.other
wished to restrict access to the resource to requests only from https://foo.example
, (i.e no domain other than https://foo.example
can access the resource in a cross-origin manner) they would send:
Access-Control-Allow-Origin: https://foo.example
CORS effect on Cookies
The most interesting capability exposed by both XMLHttpRequest
and CORS is the ability to make "credentialed" requests that are aware of Cookies and HTTP Authentication information. By default, in cross-origin XMLHttpRequest
invocations, browsers will not send credentials (i.e., cookies). A specific flag has to be set on the XMLHttpRequest
object when it is invoked.
Consider the following example:
const invocation = new XMLHttpRequest();
const url = 'https://bar.other/resources/credentialed-content/';
function callOtherDomain() {
if (invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
}
If we want to send Cookies to the server then withCredentials = true
needs to set on the XMLHttpRequest instance. And if the server responds with some cookie, then the server also needs to include Access-Control-Allow-Credentials: true
header along with Access-Control-Allow-Origin
not being a wildcard (i.e., it shouldn't be "*
"). Only then, the response and the response cookies will be made available to the JavaScript code. Otherwise, a CORS error will be printed on the devtools console.
The following example explains it:
Here is a sample exchange between client and server:
GET /resources/credentialed-content/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: https://foo.example/examples/credential.html
Origin: https://foo.example
Cookie: pageAccess=2
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
[text/plain payload]
Although line 10 contains the Cookie destined for the content on https://bar.other
, if bar.other
did not respond with an Access-Control-Allow-Credentials: true
(line 16), the response would be ignored and not be made available to the web content. Also notice the following: Access-Control-Allow-Origin: https://foo.example
. If it was Access-Control-Allow-Origin: *
, then browser would have not allowed the JavaScript to access the response or the response cookies as explained earlier.