Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user's web browser to perform an unwanted action on a trusted site when the user is authenticated. A CSRF attack works because browser requests automatically include all cookies including session cookies. Therefore, if the user is authenticated to the site, the site cannot distinguish between legitimate requests and forged requests.
The impact of a successful CSRF attack is limited to the capabilities exposed by the vulnerable application and privileges of the user. For example, this attack could result in a transfer of funds, changing a password, or making a purchase with the user's credentials. In effect, CSRF attacks are used by an attacker to make a target system perform a function via the victim's browser, without the victim's knowledge, at least until the unauthorised transaction has been committed.
In short, the following principles should be followed to defend against CSRF:
Check if your framework has built-in CSRF protection and use it
If framework does not have built-in CSRF protection add CSRF tokens to all state changing requests (requests that cause actions on the site) and validate them on backend
Always use SameSite Cookie Attribute for session cookies
Implement at least one mitigation from Defense in Depth Mitigations section
Consider implementing user interaction based protection for highly sensitive operations
Remember that any Cross-Site Scripting (XSS) can be used to defeat all CSRF mitigation techniques!
See the OWASP XSS Prevention Cheat Sheet for detailed guidance on how to prevent XSS flaws.
Do not use GET requests for state changing operations.
If for any reason you do it, you have to also protect those resources against CSRF
JavaScript Guidance for Auto-inclusion of CSRF tokens as an AJAX Request header
The following guidance considers GET, HEAD and OPTIONS methods are safe operations. Therefore GET, HEAD, and OPTIONS method AJAX calls need not be appended with a CSRF token header. However, if the verbs are used to perform state changing operations, they will also require a CSRF token header (although this is bad practice, and should be avoided).
The POST, PUT, PATCH, and DELETE methods, being state changing verbs, should have a CSRF token attached to the request. The following guidance will demonstrate how to create overrides in JavaScript libraries to have CSRF tokens included automatically with every AJAX request for the state changing methods mentioned above.
Storing the CSRF Token Value in the DOM
A CSRF token can be included in the <meta> tag as shown below. All subsequent calls in the page can extract the CSRF token from this <meta> tag. It can also be stored in a JavaScript variable or anywhere on the DOM. However, it is not recommended to store it in cookies or browser local storage.
The following code snippet can be used to include a CSRF token as a <meta> tag:
<meta name="csrf-token" content="{{ csrf_token() }}">
The exact syntax of populating the content attribute would depend on your web application's backend programming language.
Overriding Defaults to Set Custom Header
Several JavaScript libraries allow for overriding default settings to have a header added automatically to all AJAX requests.
XMLHttpRequest (Native JavaScript)
XMLHttpRequest's open() method can be overridden to set the anti-csrf-token header whenever the open() method is invoked next. The function csrfSafeMethod() defined below will filter out the safe HTTP methods and only add the header to unsafe HTTP methods.
This can be done as demonstrated in the following code snippet:
<script type="text/javascript">
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS)$/.test(method));
}
var o = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(){
var res = o.apply(this, arguments);
var err = new Error();
if (!csrfSafeMethod(arguments[0])) {
this.setRequestHeader('anti-csrf-token', csrf_token);
}
return res;
};
</script>
Axios
Axios allows us to set default headers for the POST, PUT, DELETE and PATCH actions.
<script type="text/javascript">
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
axios.defaults.headers.post['anti-csrf-token'] = csrf_token;
axios.defaults.headers.put['anti-csrf-token'] = csrf_token;
axios.defaults.headers.delete['anti-csrf-token'] = csrf_token;
axios.defaults.headers.patch['anti-csrf-token'] = csrf_token;
// Axios does not create an object for TRACE method by default, and has to be created manually.
axios.defaults.headers.trace = {}
axios.defaults.headers.trace['anti-csrf-token'] = csrf_token
</script>
JQuery
JQuery exposes an API called $.ajaxSetup() which can be used to add the anti-csrf-token header to the AJAX request. API documentation for $.ajaxSetup() can be found here. The function csrfSafeMethod() defined below will filter out the safe HTTP methods and only add the header to unsafe HTTP methods.
You can configure JQuery to automatically add the token to all request headers by adopting the following code snippet. This provides a simple and convenient CSRF protection for your AJAX based applications:
<script type="text/javascript">
var csrf_token = $('meta[name="csrf-token"]').attr('content');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("anti-csrf-token", csrf_token);
}
}
});
</script>
Related reading