What is a Cross-Site Request Forgery Attack & How to Prevent It

When you are browsing a website, it is typical 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 it is actually being embedded from video streaming websites such as youtube.com. That’s how Content Delivery Networks (CDNs), which are used to deliver content faster work. Many websites store scripts, images and other bandwidth hungry resources on CDNs, so when you are browsing them the images and script files are downloaded from a CDN source near you rather than from the website itself.

While all the above use cases are necessary for a good browsing experience, they might also be a source of a security problem, because the website you are browsing can request your web browser to retrieve data from another website without your consent. If such requests are not handled correctly, an attacker can launch a Cross-Site Request Forgery attack.

How Can Cross-Site Request Be Dangerous?

When a website requests data from another website on behalf of a user, as long as the request is an unauthenticated one, i.e. the session cookie is not sent, there are no security concerns. Though when the user’s session cookie is sent with the request, an attacker can launch a Cross-Site Request forgery attack, which abuses the trust relationship between the victim’s browser and the web server.

In this article, we will highlight the dangers that arise from this browser behaviour and how an attacker can take advantage of it and carry out a Cross-Site Request Forgery attack. In order to be able to understand how Cross-Site Request Forgery works, one must understand what HTTP requests and cookies are, so let’s start with a quick introduction of them first.

A Quick Introduction to HTTP Requests & Web Cookie

GET and POST HTTP Requests

HTTP GET Request

A HTTP GET request is when you request a web server data. Example; when you enter the URL in the web browser you instruct the browser to send a HTTP GET request to the web server on which the website is hosted, so the web server in return sends the response for the browser to render it.

HTTP POST Request

A HTTP POST request is when you send data to be posted on the web application. Example; when you submit a web form, which could be a login or contact form, your browser generates a HTTP POST request. The HTTP POST request contains the data you submitted through the web form.

Automatically Generated HTTP GET and POST Requests

Sometimes GET and POST requests are triggered automatically via certain HTML tags, or by a javascript. Therefore they not always require user interactions. Example; the <img> tag automatically generates a GET request to the image link declared inside its src attribute. Another example is an XHR POST request, which is used to automatically fetch search suggestions when the user is typing in 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 and 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 Short Introduction to Web Forms

A Short Introduction to Web Forms

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 on "change" while you are logged in. If you are not logged in, i.e. you do not have a session cookie the form would not 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>

Under the hood

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 (insert, update, delete etc.), such as an indicator of the current page you are browsing; ?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

Upon submitting this form, the browser sends the below 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, which the web application uses to determine whether or not you are logged in etc.

Abusing Web Forms in a Cross-site Request Forgery Attack

My new email address

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_form" method = "POST" action = "http://example.com/email.php">
            <input type = "text" name = "mail" value = "bob@attacker.com">
<form>
<script>
            document.getElementById('csrf_form').submit();
</script>

The self submitting form on 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 your knowledge and consent. Instead the button is replaced by one line of javascript:

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

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

The Attacker’s HTTP Request

The 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 browser issues a POST request
  • The host is the vulnerable website the user is logged in to, in our case example.com. Note the referrer and origin header, which shows from where the request is coming, i.e. 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 logged in 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 is the post body, the parameter-value pair. The parameter is mail and the value is the attacker’s set one, 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 is trick the user to navigate to that URL, which is typically done by encouraging the victim to visit the URL via an email.

So once the victim visits https://attacker.com/csrf.html the form submission is triggered. The vulnerable website https://example.com accepts the request and the mail 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 using the "password reset" functionality to send a password reset mail. Since the email was changed to bob@attacker.com, bob will receive the email and can easily change Alice's password. Therefore he completely took over her account and even locked her out.

Hiding the CSRF Attack from the Victim

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

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

Preventing 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

This is a random string that is only known by the user’s browser and the web application. It is usually stored inside a session variable. It is typically on a page inside a hidden form field which is sent together with the request.

If the value 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. Since 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, he cannot launch a CSRF attack.

Use the Same-Site Flag in Cookies

The Same-Site Flag in Cookies is a relatively new method that is being used to prevent CSRF attacks. 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 and the web application uses it to distinguish different users from each other, and to determine if you are logged in.

If the session cookie is marked as Same-Site 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/ cannot make POST requests to https://example.com/post_comment.php, since the session cookie is not sent along with the request, because it originates from a different domains.

Vulnerability Classification and Severity Table

Classification ID / Severity
PCI v3.1 6.5.9
PCI v3.2 6.5.9
OWASP 2013 A8
CWE 352
CAPEC 62
WASC 9
HIPAA 164.306(a)
Netsparker Low

Netsparker

Dead accurate, fast & easy-to-use Web Application Security Scanner

DOWNLOAD DEMO TRY ONLINE SCAN