Dealing with some CSRF attacks using the SameSite cookies

JavaScript

Keeping our tokens in cookies can have significant advantages over using the Web Storage API. If we use the HttpOnly attribute, we can’t access the cookies through JavaScript. That means that any malicious code won’t be able to do that too.

In the above scenario, we assume that the browser automatically sends the cookies when it performs requests. No need to attach the token manually. This mechanism has its caveats, though. We depend on the browser to send the cookies. Therefore, it might do that in inappropriate moments.

In this article, we explore what a Cross-Site Request Forgery (CSRF) is and how we can decrease its impact by managing our cookies better.

What a Cross-Site Request Forgery attack is

With Cross-Site Request Forgery (CSRF), an attacker forces the victim to perform unintentional actions. If the victim is currently authenticated on a website, the consequences can be severe. Let’s go through a straightforward example of the CSRF attack.

Imagine receiving a message from your friend with a link. You open it, not knowing that the account of your friend has been hacked. The malicious site that you visit can attempt a CSRF attack, hoping that your browser still has the cookies from the time you used your online banking account.

Unfortunately, the attacker can find a way to send a request from a malicious website to the bank and steal your funds. That is, if your bank has security flaws, that won’t stop such attacks.

Let’s say that the attacker figured out the exact URL in the bank’s API that transfers the funds from one account to the other. The hostile website wouldn’t use the Fetch API or XHR to send an Ajax request because it wouldn’t contain your cookies. After all, they belong to the origin of the bank.

A possible thing to do would be to redirect the victim from the infected site to the bank.

Running the above code would result in sending a GET request to the provided URL. Depending on the configuration of the cookies, the browser might attach them to the request. It would be a simple Cross-Site Request Forgery attack by forcing the user to perform an unwanted action.

Sending requests other than HTTP GET

Usually, the GET requests don’t have much impact on the application. Their job is just to return the data. Unfortunately, the attacker can forge a POST request without using the XHR or Fetch API.

To do that, the malicious site needs to contain a form element.

Clicking on the “Hack me” button redirects the user to the page with a POST request. It also appends the field as form data. Depending on the configuration of the cookies, it can also append them. This might wreak havoc if not for the two-factor authentication. The two-factor authentication could be bypassed, though.

The attacker has a few ways of improving the above code, so beware. The first thing would be sending the form automatically, without the need to click on the “Hack me!” button.

Also, a lot of the APIs nowadays use JSON to communicate, instead of . Fortunately, the attacker can’t mark the data as . With hacking the form value a bit, the attacker can prepare a malicious request, though.

If our server accepts it even though it is marked as instead of , it is a potential issue.

Using SameSite cookies

In the previous section, we’ve often mentioned that whether the browser sends the cookies or not depends on the configuration. The attribute that can affect this behavior is called . It is a part of the HTTP response header. It allows us to specify if the browser should send the cookies when the request is initiated from another domain.

If you want to know more about cookies and the Set-Cookie header, check out Cookies: explaining document.cookie and the Set-Cookie header

SameSite=None

If we set the attribute, the browser sends the cookies in all contexts. Here, the browser sends the cookies both with and the request we initiate through the elements.

We refer to cookies matching the domain of the current site as the first-party cookies. We call cookies from domains other than the current site third-party cookies. If we use an iframe to embed in , the browser considers it a cross-site context. Since we’ve marked the cookies with the attribute, the browser sends them with each matching request.

Another case worth noting is that the browser also sends the cookies marked with when requesting images from another domain.

The attribute requires us to also set the   attribute. With it, we can only send cookies with through HTTPS

SameSite=Lax

When we send the attribute when setting the header, the above behavior changes. With it, the browser does not send cookies with cross-site requests as long as the user is not navigating to the origin site.

If we have an iframe that embeds in , the browser does not send the cookies even if we were previously authenticated in the . Since recently became the default value in modern browsers, it broke some solutions that we could have encountered on the web.

An attacker might still perform some of the attacks mentioned in this article. The browser sends cookies when navigating to other sites. Therefore, using still causes the browser to send cookies with a GET request. Therefore, it might not be a good idea to design our API so that GET requests could be malicious. A similar situation is with the elements with and the elements.

The browser also does not send the cookies with POST requests made with elements or when requesting images.

SameSite=Strict

The is the most secure of all possible settings. With it, the browser sends the cookies only from a first-part context.

This means that the browser does not send the cookies when using . The same thing applies to anchor elements and GET requests we could send with the element.

Also, browsers do not send cross-site requests from within iframes, similarly to cookies with .

Summary

In this article, we’ve looked into some examples of CSRF attacks and how we could counter them. We’ve seen all of the possible configurations and how they differ. It is also good to know that browsers recently shifted to using by default. Some applications became more secure out of the box, but some solutions might have broke.

Making sure that our application is secure is one of the crucial jobs of a developer. Therefore, it is definitely worth knowing how our application could be attacked and how to deal with it.

Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Maciej Solnica
Maciej Solnica
2 years ago

Love this article! Thanks a lot! I really appreciate the straight forward way of describing topics on your blog 😀