What a Cross-Site Request Forgery Attack Is and How to Prevent It
Learn what you as a developer can do with HTTP requests and cookies to keep your web app secure and prevent a CSRF attack.
Join the DZone community and get the full member experience.
Join For FreeWhen 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 behavior and how an attacker can take advantage of it and carry out a Cross-Site Request Forgery attack. In order 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 and Web Cookies
GET and POST HTTP Requests
HTTP GET Request
An HTTP GET request is when you request a web server's data. For example, when you enter the URL in the web browser you instruct the browser to send an 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
An HTTP POST request is when you send data to be posted on the web application. For example, when you submit a web form, which could be a login or contact form, your browser generates an 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 piece of JavaScript. Therefore, they do not always require user interaction. For 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, even if the web server recognizes them based on their 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
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.1mail=alice@example.com
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 to which the form is submitted. 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/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 where the request is coming from, 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 use 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 a 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 domain.
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 |
Published at DZone with permission of Sven Morgenroth, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments