What is cross-site request forgery?

What is cross-site request forgery? Cross-site request forgery attacks (CSRF or XSRF for short) are used to send malicious requests from an authenticated user to a web application. Learn how CSRF attacks work and what we can do to prevent them.

What is cross-site request forgery?

Cross-site request forgery attacks (CSRF or XSRF for short) are used to send malicious requests from an authenticated user to a web application. The attacker can’t see the responses to the forged requests, so CSRF attacks focus on state changes, not theft of data. Successful CSRF attacks can have serious consequences, so let’s see how CSRF works and how you can prevent it.

Legitimate Cross-Site Requests 

When you are browsing a website, it is common for that website to request data from another website on your behalf. For example, in most cases, a video that is shown on a website is not typically stored on the website itself. The video appears to be on the website but is actually being embedded from a video streaming site such as YouTube. That’s the idea behind Content Delivery Networks (CDNs), which are used to deliver content faster. Many websites store scripts, images, and other bandwidth-hungry resources on CDNs, so during browsing, images and script files are downloaded from a CDN source rather than the website itself.

While this improves the browsing experience, it might also be a source of a security problem if a website asks the web browser to retrieve data from another website without the user’s consent. If such requests are not handled correctly, an attacker can launch a cross-site request forgery attack – a type of attack that has made the OWASP Top 10 list of most critical web application security flaws several times.

How Can Cross-Site Requests Be Dangerous?

When a website requests data from another website on behalf of a user, there are no security concerns as long as the request is unauthenticated, i.e. the session cookie is not sent. However, when the user’s session cookie is sent with the request, attackers can launch a cross-site request forgery attack that abuses the trust relationship between the victim’s browser and the web server.

Combined with social engineering to persuade users to open a malicious link, CSRF attacks can have serious consequences – but how exactly do they work? Before we discuss CSRF vulnerabilities, let’s start with an overview of HTTP requests, cookies, and user sessions.

A Quick Introduction to HTTP Requests and Cookies

HTTP GET Requests

HTTP GET requests are used to request data from the web server. For example, when you enter a website URL in your web browser, you instruct the browser to send an HTTP GET request to the web server that hosts the website. The server then returns the response, and the browser renders it.

HTTP POST Requests

HTTP POST requests are used to send data to be posted on the web application. For example, when you submit a web form, such as a login or contact form, your browser generates an HTTP POST request. The request contains data submitted through the web form.

Automatically Generated HTTP GET and POST Requests

Sometimes, GET and POST requests are triggered automatically by certain HTML tags or by JavaScript, so they don’t always require user interaction. For example, the <img> tag automatically generates a GET request to the image link declared in the img src attribute. Another example is an XHR POST request (AJAX request), used to automatically fetch search suggestions while the user is typing a query.

Web and Session Cookies

Websites use cookies to identify a user, or to retain a user’s logged in session on the website. Session cookies typically contain a unique ID, which is an identifier the web application uses to identify a particular logged in user. Therefore, when a cookie is set for a specific website, the web browser sends it along with every HTTP request it issues to that website to retain the logged in session. However, this is not just a matter of session cookies. Using CSRF, an attacker can also issue requests on behalf of a user who uses NTLM or Basic Auth for authentication, even if the web server recognizes him based on his IP address.

How Does Cross-Site Request Forgery Work?

Since cross-site requests do not need your permission, an attacker can abuse this and send requests without your consent and without you noticing. Let’s take a look at the following scenario which highlights why this can be a problem.

A Legitimate Web Form

Imagine a form on a website that allows you to change your email address. To change it, all you have to do is write your new address into an input field and click “Change” while you are logged in. If you are not logged in, you do not have a session cookie, and the form won’t work. Let’s see how this input field works by looking at the underlying HTML code:

<body>
    <p>Your new email address</p>
    <form method="POST" action="mail.php">
    <input type="text" name="email" 
        placeholder="Change your email address">
    <button type="submit">Change</button>
    </form>
</body>

In the web form code above, there are three important HTML attributes: method, action and name.

The method Attribute

The method HTML attribute is used to specify which HTTP verb to use, and by default it is GET. When GET is used, all the parameter-value pairs are sent as part of the request in the URL, therefore appearing in the browser bar right after the question mark character, such as:

https://www.example.com/mail.php?email=alice@example.com

Such requests are logged in the web server’s log files and in the browser history. Therefore, the GET verb should only be used to transmit non-sensitive information and for actions that don’t change data (so no insert, update, or delete). For example, it could be used to indicate the page you are currently browsing, such as ?page=home.

In web forms, typically the HTTP verb POST is used, because it sends the data in the body of the request. Therefore there is no record of the data being sent in server logs or browser history. This makes it ideal for transmitting sensitive and large amounts of data, such as passwords.

The action Attribute

The second HTML attribute is called action, in which the target of the request is specified. This can either be a page on the website or an external one, on another domain. The value in the above code sample is mail.php, which is the name of the PHP script that allows you to change your email address.

The name Attribute

The name attribute in the web form’s input field that contains the name of the parameter in which the data you submit will be stored. Therefore, if you type in alice@example.com in the form and click the submit button, the value of the parameter mail will be set to alice@example.com and the web server will receive the following parameter-value pair:

mail=alice@example.com

The HTTP POST Request

When the form is submitted, the browser sends the following HTTP request:

POST /email.php HTTP/1.1
Host: example.com
Cookie: SESSION=e29a31e41c9512a4bd
Content-Type: application/x-www-form-urlencoded

mail=alice@example.com

As you can see, there is a Host HTTP header containing the hostname of the website the form is submitted to. There is also a Cookie HTTP header containing the session cookie value which the web application uses to determine if you are logged in.

Abusing Web Forms in a Cross-site Request Forgery Attack

Create a Copy of the Web Form

With the above knowledge, it is easy to think of a scenario in which an attacker can change your email address to something of his choice. He can copy the web form and host it on his own web server, such as http://www.attacker.com/evil.php. Below is an example of the new form:

<form id="csrf" method="POST" action="http://example.com/email.php">
    <input type="text" name="mail" value="bob@attacker.com">
<form>
<script>
    document.getElementById('csrf').submit();
</script>

The self-submitting form is then placed at https://attacker.com/csrf.html.

From the code above, you can also notice that the form does not have a “Submit” button, so the attacker can trigger the web form without the user’s knowledge or consent. Instead of a button, we have one line of JavaScript:

document.getElementById('csrf').submit();

This JavaScript code first selects the form element and then submits the form. Once the form is submitted, the victim’s email address is changed to bob@example.com, as set in the value attribute in the code above.

The Attacker’s HTTP Request

Below is the HTTP Post request that was generated by the attack. We’ve removed some unrelated request headers so it is easier to read:

POST /email.php HTTP/1.1
Host: example.com
Origin: http://attacker.com
Referer: http://attacker.com/csrf.html
Cookie: SESSION=e29a31e41c9512a4bd
Content-Type: application/x-www-form-urlencoded

mail=bob@example.com

Here is a breakdown of the attacker’s HTTP POST request:

  • The web browser issues a POST request.
  • The host is the vulnerable website the user is logged in to, in our case example.com. Note the Origin and Referer headers that show where the request is coming from – the referrer is attacker.com.
  • The Cookie header, which contains the user’s session cookie. Even though the browser request was initiated by a malicious script, the browser still sends the Cookie header along with the request because the request is for example.com, for which the user has a session cookie. This also means that the web application will recognize the user’s authorized session.
  • Below the cookie header is the Content-Type HTTP header which shows that the request was issued by a form.
  • And at the bottom, as the post body, is the parameter-value pair. The parameter is mail and the value is the one set by the attacker: bob@attacker.com.

Tricking Victims Into a CSRF Attack

How can attackers trick their victims into a CSRF attack? Let’s assume the attacker publishes the form on https://attacker.com/csrf.html. All he needs to do now is trick the user into navigating to the malicious website. This usually involves a little social engineering, for example sending a phishing email asking the victim to urgently visit this URL to restore access to their bank account.

So once the user visits https://attacker.com/csrf.html, the form submission is triggered. The vulnerable website https://example.com accepts the request and the email is changed to bob@attacker.com, since to the web application it seems like the victim submitted the form (because of the session cookie).

Now all an attacker has to do is use the password reset functionality to send a password reset email. Since the email was changed to bob@attacker.com, Bob will receive the email and can easily change Alice’s password to lock her out and take over her account.

Hiding the CSRF Attack from the Victim

For the victim not to notice the CSRF attack, the attacker can create another web page, for example info.html, which contains information about a topic the victim is interested in. However, the web page controlled by the attacker can contain a hidden iframe pointing to https://attacker.com/csrf.html. When the user visits info.html, the form on csrf.html is automatically triggered, without any visible indicator to the victim.

Attackers typically use CSRF attacks in login forms, such as password or email change forms, to hijack their victims’ accounts or create a new admin user on a web application.

How to Prevent Cross-Site Request Forgery Attacks

An attacker can launch a CSRF attack when he knows which parameters and value combination are being used in a form. Therefore, by adding an additional parameter with a value that is unknown to the attacker and can be validated by the server, you can prevent CSRF attacks. Below is a list of some of the methods you can use to block cross-site request forgery attacks.

Implement an Anti-CSRF Token

An anti-CSRF token is a type of server-side CSRF protection. It is a random string that is only known to the user’s browser and the web application. The anti-CSRF token is usually stored inside a session variable. On a page, it is typically in a hidden field that is sent with the request.

If the values of the session variable and the hidden form field match, the web application accepts the request. If they do not match, the request is dropped. In this case, the attacker does not know the exact value of the hidden form field that is needed for the request to be accepted, so he cannot launch a CSRF attack. In fact, due to same origin policy, the attacker can’t even read the response that contains the token.

Use the SameSite Flag in Cookies

The SameSite flag in cookies is a relatively new method of preventing CSRF attacks and improving web application security. In the above scenario, we saw that https://attacker.com/ could send a POST request to https://example.com/ together with a session cookie. This session cookie is unique for every user, so the web application uses it to distinguish users and to determine if they are logged in.

If the session cookie is marked as a SameSite cookie, it is only sent along with requests that originate from the same domain. Therefore, when https://example.com/index.php wants to make a POST request to https://example.com/post_comment.php, it is allowed. However, https://attacker.com/ can’t send POST requests to https://example.com/post_comment.php, since the session cookie originates from a different domain, so it is not sent along with the request.

Vulnerability Classification and Severity Table

Classification ID / Severity
PCI v3.2 6.5.9
OWASP 2013 A8
OWASP 2017 A5
CWE 352
CAPEC 62
WASC 9
HIPAA 164.306(a)
ISO27001 A.14.2.5
Invicti Low
Zbigniew Banach

About the Author

Zbigniew Banach - Technical Content Lead & Managing Editor

Cybersecurity writer and blog managing editor at Invicti Security. Drawing on years of experience with security, software development, content creation, journalism, and technical translation, he does his best to bring web application security and cybersecurity in general to a wider audience.