Common CSRF Prevention Misconceptions
At NCC Group we’ve noticed, among applicants and the general public, some common misconceptions regarding Cross-Site Request Forgery (CSRF) and how to prevent it. This is the result of a failure to fully understand the following concepts and the relations between them:
- Cross-Origin Resource Sharing (CORS), a browser policy that enables cross-domain requests.1
- The Same Origin Policy, a security mechanism provided by web browsers that prevents applications hosted on two different domains from accessing each other’s data.2
- The difference between XML HTTP Requests (XHR) and HTML form submissions.
This can result in some ill-advised beliefs about CSRF defenses, such as the following:
- That CORS is a defense against CSRF.
- That allowing only specific request methods can provide a reliable defense against CSRF.
- That certain Content-Type headers make CSRF impossible.
We will examine each of these below, and introduce an alternative.
CORS Is Not a CSRF Prevention Mechanism
Cross-Origin Resource Sharing (CORS) is a mechanism that enables applications hosted on two different domains to share resources: it is important to realize that CORS is a relaxing of the Same Origin Policy. When a server sets a CORS policy, it instructs the browser to modify its normal behavior to allow the sending of requests and reception of server responses across origins. If the browser determines, on the basis of headers, request method, and content type, that a request is not a simple request, then it will send a preflight OPTIONS request before sending the actual request. The browser will then allow or deny the request on the basis of the CORS policy communicated in the preflight response.3
While a properly configured CORS policy is important, it does not in itself constitute a CSRF defense. Adding a CORS policy where there was none before cannot confer any additional protection against CSRF. In fact, an overly permissive CORS policy can undermine some attempts to prevent CSRF, as we will see below. Also, CSRF via a simple request will succeed or fail independent of CORS policies.
Do Not Rely on Alternative HTTP Methods
Browsers will always send a preﬂight request for any request method other than GET, POST, or HEAD. This means that if the server does not process any GET or POST requests, that can provide a way of protecting against CSRF by relying on a restrictive or absent preﬂight response Access‐Control‐Allow‐Origin header.
But this approach to preventing CSRF is dubious, and relies on leveraging information that is not intended for security. Because of this, it can run into problems. For example, many web application frameworks provide method-override middleware. These will typically look for an
_method query string parameter, and change the request method based on the value of that parameter.4,5 Attackers could use this method to trick the server into accepting a GET or POST request as if it were a non-simple request.
Do Not Rely on Content-Type Headers
One of the conditions that determine whether a request is a simple request, and consequently whether a preflight request is sent, is the Content-Type header. If that header has a value other than
text/plain, then the request is preflighted. Thus requiring all requests to have another content type, such as
application/json. This can provide a means of protecting against CSRF by relying on a restrictive or absent preflight response Access-Control-Allow-Origin header in a manner similar to requiring a specific HTTP method. Some frameworks may provide CSRF defenses, but disable them in these cases.6
This approach to preventing CSRF is similarly dubious; it also relies on leveraging information that is not intended for security. Changes to browser features or policies can undermine such a defense. An example of this is the
Navigator.sendBeacon API in Chrome. A longstanding bug in this feature allowed Content-Type headers other than the three mentioned above to be set without triggering the preflight request. Browser architects even considered changing browser behavior by making them ignore Content-Type headers when determining whether a preflight should be sent, despite the consequences this would have for applications that rely on it.7
The Alternative: Anti-CSRF Tokens
A more reliable and widely used means of preventing CSRF is to send a token, associated with the active session, with each state-chaging request. In the case of HTML forms, the server-side application includes a hidden parameter containing a session-specific token. When the form is submitted, the token is sent along in the request body. The server verifies that the received token is valid and the one expected for the current session prior to processing the request. The process is very similar for XHR requests: a custom header, or request parameter, can be set that has a value equal to a token generated by the server.8
An important pitfall to watch out for is putting the token in the URL, which typically happens with GET requests, as they have no body. This behavior may expose anti-CSRF token values as URLs may be exposed in various places, including browser history, log files, and Referer headers. Because GET requests should not perform state-changing actions, there should be no need to ever send anti-CSRF tokens using this method. In such a situation, the right approach would be to evaluate which HTTP method, other than GET, is appropriate when performing the action.
Don’t Take Shortcuts When It Comes to CSRF
The misconceptions described above can lead to inadequate CSRF defenses. Sometimes, a desire to avoid the complexity of token-based defenses can make alternatives more attractive. But CSRF is a serious vulnerability, and it’s important to use a robust and future-proof CSRF defense, as taking shortcuts will backfire sooner or later. A full exploration of token-based defenses is beyond the scope of this post, but a thorough introduction can be found in Cross Site Request Forgery: An introduction to a common web application weakness. Also, most web application frameworks have such protections built-in. These should be used whenever possible, and token-based defenses should be chosen in favor of the alternatives described above.
Published date:  05 September 2017
Written by:  Angelo Mellos