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
Web and Session Cookies
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:
<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>
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:
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 email@example.com in the form and click the submit button, the value of the parameter mail will be set to firstname.lastname@example.org and the web server will receive the following parameter-value pair:
The HTTP POST Request
Upon submitting this form, the browser sends the below HTTP request:
POST /email.php HTTP/1.1
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 = "email@example.com">
The self submitting form on https://attacker.com/csrf.html
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
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, firstname.lastname@example.org.
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 email@example.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 firstname.lastname@example.org, 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|