CSRF protection with CORS Origin header vs. CSRF token CSRF protection with CORS Origin header vs. CSRF token javascript javascript

CSRF protection with CORS Origin header vs. CSRF token


know, that this should not be possible with XHR (see e.g. Security for cross-origin resource sharing), at least not, if we trust the W3C spec to be implemented correctly in all modern browsers (can we?)

At the end of the day you have to "trust" the client browser to safely store user's data and protect the client-side of the session. If you don't trust the client browser, then you should stop using the web at all for anything other than static content. Even with using CSRF tokens, you are trusting the client browser to correctly obey the Same Origin Policy.

While there have been previous browser vulnerabilities such as those in IE 5.5/6.0 where it has been possible for attackers to bypass the Same Origin Policy and execute attacks, you can typically expect these to be patched as soon as discovered and with most browsers automatically updating, this risk will be mostly mitigated.

But what about other kinds of requests - e.g. form submit? Loading a script/img/... tag? Or any other way a page can use to (legally) create a request? Or maybe some known JS hack?

The Origin header is normally only sent for XHR cross-domain requests. Image requests do not contain the header.

Note: I am not talking about

  • native applications,

  • manipulated browsers,

  • cross site scripting bugs in example.com's page,

I'm not sure whether this falls under manipulated browsers or not, but old versions of Flash allowed arbitrary headers to be set which would enable an attacker to send a request with a spoofed referer header from the victim's machine in order to execute an attack.


Web content can't tamper with the Origin header. Furthermore, under the same origin policy, one origin can't even send custom headers to other origins. [1]

Thus, checking the Origin header is just as good at blocking attacks as using a CSRF token.

The main concern with relying on this is whether it it lets all legitimate requests work. The asker knows about this issue, and has set up the question to rule out the major cases (no old browsers, HTTPS only).

Browser vendors follow these rules, but what about plugins? They might not, but the question disregards "manipulated browsers." What about bugs in the browser that let an attacker forge the Origin header? There can be bugs that allow the CSRF token to leak across origins too, so it would take more work to argue that one is better than the other.


Explaining the terms

I think the question should be same-origin policy vs CSRF token. Because CORS is a mechanism to allow two different domains to talk to each other (by relaxing same-origin policy), whereas same-origin policy and CSRF token restrict domains to talk to each other.

GET methods are never save

All browsers implement the same-origin policy. This policy avoids in general that a web application on domain A can make a HTTP request to an application on domain B. However, it does not restrict all requests. For example same-origin policy does not restrict embed tags like this:

<img src="https://dashboard.example.com/post-message/hello">

It’s irrelevant whether the response is a valid image — the request is still executed. This is why it’s important that state-changing endpoints on your web application cannot be invoked with the GET method.

Preflight check

You may use CORS to avoid same-origin policy and let the domain A make a request to domain B that would otherwise be forbidden.Before the actually request is send, a preflight request will be send to check if the server allows domain A to send this request type. If it does, domain A will send the original request.

For example, if no CORS are set, then a Javascript XMLHttpRequests would be restricted for domain A by a preflight, without executing the request on domain B.

Why you need CSRF token despite same-origin policy

If same-origin policy would work for all types of request then you would be right and there is no need to use CSRF token, because you would have full protection by the same-origin policy. However, this is not the case. There are a couple of HTTP requests that do not send a preflight request!

GET, HEAD and POST requests with specific headers and specific content-type do not send a preflight request. Such requests are called simple requests. This means the request will be executed and if the request was not allowed, then a not-allowed error response will be returned. But the problem is, that the simple request was executed on the server.

Unfortunately, a plain <form action="POST"> creates a simple requests!

And because of these simple requests, we have to protect the POST routes with CSRF tokens (GET routes don't need CSRF because they can be read anyway by embedded tags as shown above. Just make sure you don't have a state-changing get method).