In the original version my talk “Think Like a Hacker and Secure WordPress, live on stage“, I demonstrated a Cross-Site Request Forgery (CSRF) attack. While this attack worked perfectly during WordCamp Brisbane 2019, in-progress changes to Google Chrome (version 80) are bringing about the end of CSRF. (Well, sort of…) In light of this change, I’ve removed the CSRF demo from my talk (replacing it with a rather funky privilege escalation attack). However, I wanted to provide more information about the change. Covering both why I removed it from my talk and if CSRF is dead or not.
If you haven’t guessed (or peeked at) what the change is yet, I am talking about Chrome 80 treating Cookies as SameSite=Lax
by default when no SameSite
attribute is set. I’ll explain the technical side of this shortly, but for now all you need to know is the purpose of the SameSite
attribute is to stop CSRF attacks.
In a nutshell, this change in Chrome is designed to stop CSRF attacks. Since I use Chrome for my talk, I decided it would be better to find a new vulnerability than risk keeping this one.
But CSRF Isn’t Really Dead, Is It?
It’s worth pointing out that SameSite=Lax
doesn’t really kill CSRF attacks. According to their own reported schedule, Google Chrome is only very slowly enabling Lax
by default. Firefox is working on it, and Microsoft Edge is getting it when the change flows from the Chromium project. As for other browsers, like Safari, I don’t think they’ve announced it yet. Ultimately, only a small percentage of the internet will be browsing sites with SameSite=Lax
for a while. Also, consider how long IE6 and IE11 have stuck around, and the missing features in Safari, and we’re likely to see browsers without SameSite=Lax
for a while.
The other big problem is that SameSite=Lax
doesn’t apply to everything. Clicking a navigation link that makes a GET
request to your site will allow cookies to be set. While the expectation is that GET
requests are idempotent, this is not always the case. A flexible app framework may treat GET
and POST
parameters the same, allowing a GET
request to be used in place of a POST
. Or a the developer could forget the context of the request. Or a bug could exist. Anyway, my point is that SameSite=Lax
isn’t going to solve CSRF completly.
But don’t just take my word for it. I asked the question “Is CSRF dead now that the major browsers are going to default cookies to SameSite=Lax?” on Twitter recently, and received this from n00py:
There are other possible bypasses to explore too, depending on the situation and application you’re attacking.
So no, I don’t believe CSRF is dead.
Why Remove the Demo from Your Talk?
The short answer is because I use Chrome. I have no way of knowing when my Chrome browser will toggle on SameSite=Lax
. If Google decided to push this change to my browser an hour before my next talk, then I’ll get up on stage and my demo won’t work. I could switch to Firefox, but what if they release their update? And Edge has the same problem.
I could toggle it on in the settings, demonstrate the behaviour of SameSite=Lax
, but that adds significant complexity into the talk. I’d then need a third browser window – one without SameSite=Lax
– and have to explain the cookie attribute and the browser switch, etc. It’d definitely be possible, and would enhance the talk, but it’d also take up valuable time. I managed to fit seven vulnerabilities into that talk… just. Spending longer on one is likely to cut off my finale (a blind SQL injection). The alternative was to find a new vulnerability.
How does `SameSite=Lax` work?
There are three possible values the SameSite
attribute can be set to: None
, Lax
, and Strict
. Cookies will always be included when a request comes from the Same Site. However if a request is made from a Different Site, cookies will be blocked based on the value:
Setting | Behaviour | Explination |
---|---|---|
SameSite=None | Cookies included on all requests. | Disables SameSite protection on the cookie, which instructs the browser to always include the cookie in every request. This was the default value, prior to Chrome 80, and would be useful for cookies used for third-party purposes like trackers and page includes. |
SameSite=Lax | Cookies included on GET or Same Site requests only. | Prevents cookies from being included on any request which isn’t (supposed to be) read-only. Such as a GET request. Cookies will not be sent for POST , PUT , etc. |
SameSite=Strict | Cookies only included on Same Site requests. | Prevents cookies from being included on all requests which don’t originate from the Same Site as the cookie. |
A simple way to look at it is to consider an application you have a active login for. In this scenario, you’re on a third-party site and there are two ways to get to your app: click a link, or submit a form.
Setting | Action | HTTP Request | Result |
---|---|---|---|
SameSite=None | Click a link to open app | GET | The app opens and you’re logged in. |
SameSite=None | Submit a form to open the app | POST | The app opens and you’re logged in. |
SameSite=Lax | Click a link to open app | GET | The app opens and you’re logged in. |
SameSite=Lax | Submit a form to open the app | POST | The app opens but you’re not logged in. You click Refresh on the browser and you’re logged in. |
SameSite=Strict | Click a link to open app | GET | The app opens but you’re not logged in. You click Refresh on the browser and you’re logged in. |
SameSite=Strict | Submit a form to open the app | POST | The app opens but you’re not logged in. You click Refresh on the browser and you’re logged in. |
What Does “Same Site” Actually Mean?
The term Same Site relates to the domain name of the site you’re making the request and the domain name of the site you’re trying to go to. In most cases this is the extension/suffix, such as .net
on my site. However there is also a list of Public Suffixes, which includes domains like github.io
. All of the domains on this list have subdomains that can be issued to different people. As a result, each of these subdomains needs to be isolated from each other.
To use an example, www.valorin.net
and valorin.net
would be considered the “Same Site” by the browser. However, your-project.github.io
and my-project.github.io
are considered different. Therefore, a SameSite
cookie would be passed between www.valorin.net
and valorin.net
, but not your-project.github.io
and my-project.github.io
.
Wait a Sec, Doesn’t That Mean Things Are Going to Break?
Yup.
That’s why Chrome is rolling it out so slowly, and the other browsers are likely hoping to hang back and let Chrome take the criticism for breaking things before following suite. Basically, it’s a security feature, and adding security features always breaks things. Therefore, I also believe it’s the right move.
Summary
I hope that’s given you a bit of an insight into why my talk changed, and what the SameSite=Lax
cookie thing is about. As you’ve probably realised by now, CSRF isn’t dead, and won’t be any time soon. However, the complexities in explaining it and injecting it into my talk made it easier to pull and replace.
That said, I’m working on a new talk all about SameSite=Lax
… but given COVID-19, it may be a while before I get the opportunity to present it.
Until then, I hope this post is enough.