Cookies are very often used for session management as HTTP is a stateless protocol.
How cookies are created on the browser
- A domain can add cookies to the HTTP response. The Set-Cookie response header adds cookies to the HTTP response.
- Set-Cookie: cookie-name=cookie-value
- Any page belonging to a domain can create new cookies via JavaScript using the Document.cookie property.
ie a domain can create cookies both on the server and the client side.
Adding cookies to the HTTP request
The "Cookie" request header is added by the browser in subsequent requests to the same path from where the cookie was received.
Cookie Attributes - Attributes that determine if cookies will be added to a request based on the "origin" of the request and "where" cookies are sent.
- Domain attribute: The Domain attribute specifies "which hosts" can receive a cookie ie "where" the cookie will go. Note that as far as the domain attribute is concerned, the origin of the request does not matter. The four key rules to be remembered are
- A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix.
- If the domain attribute value is specified then the cookie will go to the domain and all subdomains including nested subdomains.
- If the domain attribute is not specified the cookie will be a host only cookie (hostonly flag will be set to true) which will go to the exact domain/subdomain which has created it.
- The setting/writing of cookies can happen on the server side when the page is generated or by javascript on the page.
- Note that In RFC 2109, a domain without a leading dot meant that it could not be used on subdomains, and only a leading dot (.domain.com) would allow it to be used across multiple subdomains (but not the top-level domain). However, all modern browsers respect the newer specification RFC 6265, and will ignore any leading dot.
Based on the above four rules let us look at a few examples/ramifications.
-
- example.com does not set the domain attribute. This will be host only cookie(with hostonly flag set to true) that will go only to example.com
- the cookie will be added to request headers in requests made to subdomain.example.com
- example.com sets the domain attribute to example.com. This cookie will go to example.com and all subdomains and nested subdomains ie
- example.com
- *.example.com (subdomains of example.com)
- In the microservices architecture, once a cookie is set by the service s1 on example.com, it will go to other services like S2 on subdomain2.example.com and S3 on subdomain2.example.com.
- *.*.example.com (nested subdomains)
- example.com sets domain attribute value to HDFC.com.
- A domain cannot set a cookie that goes to another domain. (browser blocks this) ie (icicibank.com cannot set cookies with domain as hdfc.com) as this violates RULE 1
- example.com sets domain attribute value to subdomain.example.com
- A domain cannot set a cookie that goes to a specific subdomain. (browser blocks this) as this violates RULE 1.
- subdomain.example.com does not set the domain attribute. This will be host only cookie(with hostonly flag set to true) that will go only to subdomain.example.com
- subdomain.example.com can explicitly set domain to subdomain.example.com. This cookie will be sent to
- subdomain.example.com
- *.subdomain.example.com
- This cookie will NOT be sent to example.com
- This cookie will NOT be sent to accounts.example.com
- subdomain.example.com can explicitly set domain to example.com. This cookie will be sent to
- example.com
- *.example.com (subdomains of example.com)
- *.*.example.com (nested subdomains)
- example.com does not set the domain attribute. This will be host only cookie(with hostonly flag set to true) that will go only to example.com
- SameSite attribute: Same-Site Cookie Attribute helps in controlling if cross site http requests (ajax / normal ) (eg request from b.com to a.com ) go with cookies. In other words with the help of Same-Site cookie attribute, developers can now control whether cookies are sent along with the request initiated by third party websites . If the request is not cross site, the value of same site attribute does not matter. Note that Same-Site is about what cookies are sent, not what are received. Also. SameSite cookie will not block cross origin requests. It only stops the cookies from being sent in cross site requests. Also note that cross site and cross origin is not the same . Read more at https://clarifyforme.com/posts/5177685533786112/what-is-the-difference-between-site-and-origin .So CORS headers determine if cross origin AJAX requests can be made (before CORS, javascript requests could only be made to same origin) SameSite cookie attribute determines if cookies will go with the cross site request ( ajax/regular browser request). Hence CORS headers can help in blocking CSRF "script based attacks" only. Note that browsers must send origin header when making xhr cross origin requests but not for normal page navigations. (The Origin header is also included for all requests whose method is neither GET nor HEAD) SameSite attribute can have 3 values.(None,Lax,Strict)
- SameSite=None : Cross site requests will go with cookies. If SameSite=None is set, the cookie Secure attribute must also be set.
- SameSite=Lax(default for security reasons). In a cross site request, cookies are allowed only if the user is navigating (top level navigation) to the site.(when following a link , which leads to a get request). If the user is loading image/frame then cookies will not be sent as there is no top level navigation (address in the address bar is not changing).
- In cross site requests cookies will not be sent for
- script requests
- post requests
- In cross site requests cookies will not be sent for
- SameSite=Strict : In cross site requests, cookies are completely blocked.
Cookie Attributes - Attributes that determine the lifetime
- Expires attribute: determines the lifetime of a cookie.
- Max-Age attribute: determines the lifetime of a cookie.
Cookie Attributes - Attributes that determine access
- Secure attribute: the cookie will only be sent in HTTPS requests.
- this helps against man-in-the-middle attacks.
- this will not help if the attacker has physical access to the client browser's machine.
- HttpOnly attribute: The cookie cannot be read by scripts. (Specifically Document.cookie API ) This makes storing data in cookies secure as typically local storage is accessible via scripts and hence prone to xss attack
Same-origin policy and cookies
In a nutshell, the Same-Origin-Policy says that only a.com may send script requests to a.com. Cross-origin loading of page resources is generally permitted. For example, the SOP allows embedding of images via the <img> tag. However, javascript will not be able to access the image.
While subdomains are technically different origin, the same-origin policy is relaxed when dealing with cookies. It often does not differentiate between subdomain and domain.
- Creation of cookies:
- subdomains can create cookies(script/server side) that go to domain(and all its subdomains) by explicitly setting the cookie domain to the parent domain.
- Reading cookies.
- The browser prohibits any access to document.cookie from a different origin site even though it is within the cookie’s domain. For example, accounts.example.com cannot access document.cookie of operations.example.com (these are cross origin , same site requests)
This essentially implies that same-origin-policy prevents cross-origin reads via document.cookie but does not prevent subdomains from creating/overwriting cookies that go to domain. The overwriting of cookies is explained very well in the post https://security.stackexchange.com/questions/12412/what-cookie-attacks-are-possible-between-computers-in-related-dns-domains-exa
So same-origin-policy will block
- account.example.com accessing data/dom of support.example.com (same site but cross origin)
- google.com access amazon.com data/dom(cross origin and cross site )
- If accounts.example.com wants to share data(ajax)/dom with support.example.com, it can use Javascript to set document.domain=’example.com’. The document.domain property can be set to any parent domain of the current domain, and allows any two documents that have the same document.domain to execute script in each others’ context and access each others’ DOM. If there’s a page on support.example.com that also sets document.domain=’example.com’, then blog.example.com can load that page in an iframe, and since the browser now considers them to have the same origin, each can run script in the context of the other and access the other’s data/dom.
In web applications where there is a subdomain per customer, there is no good way to stop one customer's website from creating /overwriting cookies that go to another customer's website.ie customerA.blogging.com will always be able to create a cookie that goes to customerB.blogging.com by setting the domain to blogging.com. Therefore, if you use a different subdomain per customer, you may need your server-side code to be entirely under your control and be free of vulnerabilities .Read more https://security.stackexchange.com/questions/7451/is-security-increased-by-using-a-subdomain-per-customer-in-a-web-app/7567#7567
Third party cookies
ie while the server hosting a web page sets first-party cookies, the page may contain images or other components stored on servers in other domains (for example, ad banners), which will set third-party cookies if clicked/accessed .
For example
- If you visit claryforme.com blog and on the blog there is a link to medium.com article. This would be a cross origin request (clarifyforme.com is making request to medium.com) Any cookies set by medium.com would be third party cookies.
- Any xhr request made from clarifyforme.com to any other domain would lead to third party cookies.
- Any xhr request made from clarifyforme.com/somepage to clarifyforme.com/anotherpage would lead to first party cookies.
Some browsers block third party cookies (ie third party cookies cannot be set). Chrome is planning to stop supporting third party cookies in 2022.
Other important points
The XMLHttpRequest.withCredentials property is a boolean value that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.In addition, this flag is also used to indicate when cookies are to be ignored in the response. The default is false. Note that this is true regardless of Access-Control- header values.
Cookies do not provide isolation by port Ie, cookies for a given host are shared across all the ports on that host, even though the usual "same-origin policy" used by web browsers isolates content retrieved via different ports.
Cookies do not provide isolation by scheme
- HTTP: Cookie with "Secure" will be returned only on HTTPS connections
- HTTPS: Cookie with "Secure" will be returned only on HTTPS connections
- HTTP: Cookie without "Secure" will be returned on HTTP or HTTPS connections
- HTTPS: Cookie without "Secure" will be returned on HTTP or HTTPS connections
Cross-domain cookies are not allowed (i.e. Cookies set by site A.com will never be sent to B.com). But once a cookie is set by site A.com, you can send that cookie even in cross domain requests from site B.com to A.com
Test yourself
Question > will a same-site, cross-origin request from subdomain.example.com to example.com go with cookie c1 which is a hostonly cookie?
Question > https://portswigger.net/web-security/cors/same-origin-policy. Due to legacy requirements, the same-origin policy is more relaxed when dealing with cookies, so they are often accessible from all subdomains of a site even though each subdomain is technically a different origin. You can partially mitigate this risk using the HttpOnly
cookie flag.
How will httponly cookie flag mitigate risk?
Read More
- https://jacob.hoffman-andrews.com/README/why-you-need-a-www/
- there is one critical point over here, When you host your site on example.com, you ideally to set an authentication cookie that gets sent to ‘example.com’ and not any subdomains. It turns out it’s impossible to do this in a cross-browser way. On most browsers, sending a Set-Cookie header without a domain field will do the trick. However, Internet Explorer differs on this point, and will send the cookie to all subdomains (see Q3).