Skip to main content
added 32 characters in body
Source Link

NOTE: The reason OWASP recommends CSRF tokensonly using the Origin header as a secondary measure to CSRF tokens is that the Origin header didn't yet support all common browsers when the recommendation was made. All common browsers have supported this feature for quite some time now. (It is currently ~3-4 years old)

NOTE: The reason OWASP recommends CSRF tokens as a secondary measure is that the Origin header didn't yet support all common browsers when the recommendation was made. All common browsers have supported this feature for quite some time now. (It is currently ~3-4 years old)

NOTE: The reason OWASP recommends only using the Origin header as a secondary measure to CSRF tokens is that the Origin header didn't yet support all common browsers when the recommendation was made. All common browsers have supported this feature for quite some time now. (It is currently ~3-4 years old)

Source Link

Our conversation started in the comment section, but I realized some inaccuracies in what I wrote. The sharing of cookies across domains is stricter than I thought at first. This should be a more comprehensive overview, and closer to what you might be looking for.

I will make a few assumptions:

  • You have two domains: foo.com (ui) and bar.com (api)
  • You want to prevent another domain like evil.com from causing side effects/reading responses from bar.com (CSRF + CORS protection)

Approach 1: Using CSRF tokens

This can be done using cookies, or simply using custom headers and storing the values in session storage or as a hidden input in a form. This means you manually need to send the CSRF tokens as custom headers with every request. Both from server and client.

Cookies will not be a good option here, since foo.com and bar.com are separate domains.

Limitations of cookies for different domains (that are not subdomains of the same domain)

foo.com --(request)--> bar.com

  • Any cookies stored in your browser with Domain=bar.com (that does not include SameSite=lax or SameSite=strict) will be sent along with the request assuming withCredentials=true

foo.com <--(response)-- bar.com

bar.com --(request)--> bar.com

  • Here, Set-Cookie will work. Hence a redirect from foo.com->bar.com and then back will be able to set a cookie with Domain=bar.com. (So this could be a somewhat ugly/slow workaround)

Approach 2: Checking the Origin header

This is probably the simplest/cleanest option. (You won't need CSRF tokens if you use this method).

NOTE: The reason OWASP recommends CSRF tokens as a secondary measure is that the Origin header didn't yet support all common browsers when the recommendation was made. All common browsers have supported this feature for quite some time now. (It is currently ~3-4 years old)

*.com --(request)--> bar.com (CSRF)

Make sure that you check the Origin header of the incoming request in the bar.com-server. If this is either missing or https://foo.com, the request should be accepted. Otherwise, the response should be something like 403 (Unauthorized).

*.com <--(response)-- bar.com (CORS)

The response needs to have the proper CORS headers (assuming it was accepted, and the Origin header is not missing):

// Something like this, depending on your server language response.setHeader('Access-Control-Allow-Origin', request.getHeader('Origin'))