Cross-site Request Forgery
Imagine that you are logged into your webmail account. Your browser is storing a session token as a cookie scoped to the domain of the webmail service. Now imagine that you visit questionable.example.com. What would happen if this questionable site had an iframe that loaded the inbox of your webmail provider? JavaScript running on the page can examine the current state of page elements. Can it reach into the I-frame to read your email? Thanks to the same origin policy, the answer is no. Browsers prevent JavaScript from looking at the details of elements loaded from different “origins”, so with the exception of some metadata leaking through like the size of the resulting frame and time it took to load, the contents of the embedded page are hidden from the wrapping page. The rules on which elements are protected by this policy and what constitutes a separate domain are important but outside our goal here. More information can be found at https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy.
Even though the third-party website cannot read the results of the request, that request is still sent, and by default the session cookie is sent with it. So, if the victim website has an endpoint that triggers an action that’s beneficial to an attacker, there is still a risk. Let’s say that webmail service used a POST request when it sends an email? An attacker could host a JavaScript method or hidden form that automatically sends a request to this endpoint with all the right parameters to get an email delivered to someone of the attacker’s choice, with a message written by the attacker, from the victim’s actual account and mail service. What if there’s another endpoint that creates a forwarding rule? As long as the attacker can figure out the required parameters, they could then get a copy of every message the victim receives.
To break it down, all of the following must be true to have a CSRF vulnerability:
- There must be an endpoint that performs an action beneficial to the attacker even though the attacker will not be able to read the response from the request.
- The endpoint must accept either a GET or POST request. Other HTTP methods are blocked by the browser under the same origin policy unless allowed by an “Access-Control-Allow-Origin” response header.
- The endpoint must use cookies for session authentication. Browsers do not allow JavaScript to set “Authorization” or other headers when making cross origin requests.
- All of the parameters required to complete the action must be predictable by the attacker.
For a long time, the recommended defense against CSRF attacks was to block element 4, the predictability of parameters. Sites were encouraged to add an input parameter to endpoints (or at a minimum, state changing endpoints) with a random string as its value. The website could insert a hidden form field with this value on pages that make the request, and the server can check for the correct value before processing requests received. This is commonly called an “anti-CSRF token” and many frameworks can add them and validate them automatically. This defense is still effective and will block CSRF attacks.
There are a couple of other design decisions that offer CSRF protection. Browsers will not let JavaScript add custom headers to 3rd party requests. They will also not let script set a custom content-type without…
In 2016 a new cookie attribute called “SameSite” was formally proposed in RFC 6265 (https://datatracker.ietf.org/doc/html/rfc6265). This attribute lets the server setting the cookie control when it is sent with requests based on the site that the request is originating from. Cookies are still always sent to the website that originally set the cookie. Setting the value to “strict” means that only requests initiated from the setting site will ever have the cookie included with them. A value of “Lax” means that the cookie will be sent if a user takes an action on a 3rd party page like clicking a link, but will not be sent in background requests initiated by JavaScript. And a value of “none” means there is no SameSite policy and cookies will be sent with every request to the setting site, regardless of the origin. Pretty much only advertising companies want this behavior.
Initially, the default policy for cookies that did not include the SameSite attribute when they were set was “none”, since that is how things worked before. But the plan was always to change that default behavior and beginning in 2020, new versions of Chrome, Firefox, Edge, and Opera began treating cookies with no SameSite attribute set under the “Lax” policy. However, Webkit based browsers like Safari did not follow this trend and still default to the “none” policy. So to cover Safari users, and any users out there running pre-2020 versions of other browsers (any Internet Explorer users fall into this bucket as well), websites should still manually set the SameSite attribute on their session cookies to at least Lax, if not Strict as part of there anti-CSRF defenses.
So now for the question of the day, if you set the SameSite attribute to at least Lax, can you ignore other CSRF defenses? Only if you are ok being “mostly” secure. Setting this attribute is an easy win, that will protect you as long as none of your users are using an old browser and there is no way (even through vulnerabilities) for an attacker to host a payload on any domain the scope of the cookie. Those feel like acceptable conditions in many cases, but then you start thinking about the last time anyone updated grandpa’s computer or that you can’t update the SmartTV in the conference room, and you start to find weaknesses in the assumption that all users will be protected. It is still better to combine this attribute with other defenses or designs (required headers, non-default content type, etc) in order to be sure
One final point worth noting is that although Cross-site Scripting (JavaScript Injection) shares half of its name with this attack, the two have little in common. Cross-site scripting focuses on injecting code into a website that will be executed by the user's browser. That code can do almost anything, but more often than not, it will be executing an attack against the same site it was injected into. In contrast, the vulnerability that allows Cross-site Request Forgery is not the ability to inject code or content, the attacker has permission to do this. The vulnerability is in how the targeted site accepts requests that perform sensitive actions. Because the request usually does come from another site, this attack actually deserves the "Cross-site" moniker.
Clickjacking
Clickjacking, or a user interface redress attack, is a technique where an attacker embeds the target website within a page on their own website and uses various HTML techniques to trick the user into interacting with the target website without realizing it. Since the browser will send any active session cookies when the embedded site is loaded, an attacker may be able to trick the victim into performing a sensitive operation that can only be performed while logged in. Note that browsers prevent the framing website from reading the contents of the embedded site and from accessing the associated cookies. The attacker is limited to actions that will change the state of the victim website in some way that will be beneficial to the attacker. The fundamentals of the techniques had been discussed since at least 2002, but it was not until 2008 that Jeremiah Grossman and Robert Hansen presented a fully functional attack. In 2009 and 2010 there were a handful attacks using the technique against Facebook to collect likes and click on ads for revenue, but beyond these, no other instances of weaponization have been reported in readily accessible media.
The defenses against this attack focus on preventing your website from being embedded in another website. Initially, adding JavaScript code to a site to detect if it was being embedded was recommended. This was cumbersome, so in 2011 the X-Frame-Options header was introduced. Most sites can simply add this header to all responses and eliminate the risk of clickjacking. Those sites that are designed to be embedded into specific other sites and the header can be set to allow those domains. There are sites do not care where they are framed from, and these sites must be careful to ensure that their user interface does not offer an action that could be abused by a quick checking attack. Framing behavior can also be controlled in the sites content security policy using the frame ancestors directive and this method is now replacing the X-Frame-Options header in standards.
Given the lack of exploitation over the last 15 years, experience shows that clickjacking is not a desirable technique to attackers, and therefore can be considered a lower risk than many other attacks. On the flipside, adding a response header is generally trivial, and is likely worth the minimal effort required.
Other articles in this series:
Part 1 - Overview
Part 2 - Network Sniffing
Part 3 - Token Exposure
Part 4 - JavaScript Injection (XSS)
Part 5 - Blind Session Abuse (this article)
Part 6 - Post-compromise Use
Bonus 1 - JWTs: Only Slightly Worse
Bonus 2 - Device Bound Session Tokens
Errata
See a mistake? Disagree with something?