# Cross-Site Request Forgery

**Cross-site request forgery** (CSRF) vulnerabilities can be used to trick a user into performing an unwanted action on your website.

Websites consist of a combination of client-side and server-side code. The client-side code is the HTML and JavaScript that is rendered by and executed in the browser. This client-side code allows users to navigate to other URLs, submit HTML forms, and trigger AJAX requests via JavaScript. Your server-side code will intercept the data sent in the resulting HTTP requests, and act upon it appropriately.

These server-side actions can also be triggered by *forged* HTTP requests unless you explicitly put in protective measures. A forged request is when a malicious actor tricks a victim into clicking on a link, submitting a form, or running some code that triggers an unexpected action. This malicious code is typically hosted on a website controlled by the attacker, on another domain – hence the *cross-site* part of the name.

Protecting against CSRF requires two things: ensuring that `GET` requests are **side effect free**, and ensuring that other types of request originate from your client-side code by using **anti-forgery tokens**.

## Making GET Requests Side Effect Free

Hyperlinks on the internet trigger a `GET` request to the destination URL. This means that links into your website from external domains will almost always be `GET` requests. To make sure your users don’t experience unexpected actions when clicking on links, `GET` requests should only retrieve resources from the server – never *change state* on the server. Anything that changes state – a *side effect* – should be handled by `POST`, `PUT` and `DELETE` requests, depending on whether the request is adding, updating or deleting state.

Here are some examples of side effects that should **not** be performed with `GET` requests:

* Logging in
* Logging out
* Password resets
* Sign-ups
* Posting or editing content
* Account deletion

For example, consider the following route for an “Account Deletion” action in the Flask web-server:

“`python
@app.route(‘/profile/delete’, methods=[‘GET’])
def delete_post(post_id):
user = session[‘user’]with database() as db:
db.execute(‘delete from users where id = ?’, user[‘id’])
del session[‘user’]

return redirect(‘/’)
“`

If an attacker tricks a user into clicking on a link to `/profile/delete` their account will be deleted before they are aware of what is happening. This is why it’s important to implement account deletion via `PUT` or `DELETE` requests – and to confirm the action with the user before deleting their account!

## Anti-Forgery Tokens

Once you have restricted your side effects are to non-`GET` requests, you need to protect against malicious HTTP requests that use the other HTTP verbs. `POST`, `PUT` and `DELETE` requests can still be sent to your site from HTML forms and JavaScript code hosted on other domains. It is harder for an attacker to forge these type of requests – they will need the user to do more than click on a single malicious link – but it is still possible.

To ensure that you only handle valid requests for these HTTP verbs, you need to include a **secret** and **unique** token with each HTTP response, and have the server verify that token when it is passed back in subsequent requests that has side effects.

These tokens are called **anti-forgery tokens**. Each time your server renders a page that performs sensitive actions, it should write out an anti-forgery token in a hidden HTML form field. This token must be included with form submissions, or AJAX calls. The server should validate the token when it is returned in subsequent requests, and reject any calls with missing or invalid tokens.

Anti-forgery tokens are typically (strongly) random numbers that are stored in a cookie or on the server as they are written out to the hidden field. The server will compare the token attached to the inbound request with the stored value. If the values are identical, the server will accept the valid HTTP request.

Anti-forgery tokens are easily added in Flask web-server by registering the `CSRFProtect` extension:

“`python
from flask import Flask
from flask_wtf.csrf import CSRFProtectapp = Flask(__name__)
csrf = CSRFProtect(app)
“`

Any HTML forms generated by templates should add the relevant form field as follows:

“`html
<form method=”post”>
<input type=”hidden” name=”csrf_token” value=”{{ csrf_token() }}”/>
</form>
“`

AJAX requests can similarly be amended to include anti-forgery tokens in the following manner:

“`html
<script type=”text/javascript”>
var csrf_token = “{{ csrf_token() }}”;$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader(“X-CSRFToken”, csrf_token);
}
}
});
</script>
“`

In the Django web-framework, CSRF protection is provided by the `django.middleware.csrf.CsrfViewMiddleware` class.

### Ensure Cookies Are Sent With The SameSite Cookie Attribute

The `Same-Site` cookie attribute instructs the browser whether to send cookies with requests initiated by third-party domains. Since anti-forgery tokens are typically validated by comparing the token value sent in the HTML form with a value in a cookie, they are only effective when the `Same-Site` attribute is set appropriately.

A value of `Strict` will mean than *any* request initiated by a third-party domain to *your* domain will have any cookies stripped by the browser. This is the most secure setting, since it prevents malicious sites attempting to perform harmful actions under a user’s session.

A value of `Lax` permits `GET` request from a third-party domain to *your* domain to have cookies attached – but *only* `GET` requests. With this setting a user will not have to sign in again to your site if they follow a link from another site (say, Google search results). This makes for a friendlier user-experience – but make sure your `GET` requests are side effect free!

## Further Considerations

* Make sure your cookies cannot be accessed in JavaScript, and are only sent over HTTPS. This is done by adding the `HttpOnly` and `Secure` attributes to each cookie.

* Many sites require a secondary authentication step, or require re-confirmation of login details when the user performs a sensitive action. (Think of a typical password reset page – usually the user will have to specify their old password before setting a new password.) Not only does this protect users who may accidentally leave themselves logged in on publicly accessible computers, but it also greatly reduces the possibility of CSRF attacks.

* Ensuring `GET` requests are side effect free is part of a series of design principles called *Representation State Transfer (REST)* that assign certain types of action (view, create, delete, update) to different HTTP verbs. REST insists that GET requests are used only to *retrieve* resources.

## CWEs

* [CWE-352](https://cwe.mitre.org/data/definitions/352.html)

About ShiftLeft

ShiftLeft empowers developers and AppSec teams to dramatically reduce risk by quickly finding and fixing the vulnerabilities most likely to reach their applications and ignoring reported vulnerabilities that pose little risk. Industry-leading accuracy allows developers to focus on security fixes that matter and improve code velocity while enabling AppSec engineers to shift security left.

A unified code security platform, ShiftLeft CORE scans for attack context across custom code, APIs, OSS, containers, internal microservices, and first-party business logic by combining results of the company’s and Intelligent Software Composition Analysis (SCA). Using its unique graph database that combines code attributes and analyzes actual attack paths based on real application architecture, ShiftLeft then provides detailed guidance on risk remediation within existing development workflows and tooling. Teams that use ShiftLeft ship more secure code, faster. Backed by SYN Ventures, Bain Capital Ventures, Blackstone, Mayfield, Thomvest Ventures, and SineWave Ventures, ShiftLeft is based in Santa Clara, California. For information, visit: www.shiftleft.io.

Share

See for yourself – run a scan on your code right now