Cross site request forgery (CSRF)

Cross site request forgery is a kind of attack that aims on websites which allow their users to authenticate permanently. Suppose that an attacker knows the structure of your application – he knows which HTTP request to send to perform an action on behalf of logged-in user. All he has to do is to prepare an <a> tag to click, <img> tag to display or <form> which automatically submits using JavaScript when someone visits his trap-page or views an email with his HTML code. It works because when a HTTP request is performed, the browser appends all cookies that are used to identify a visitor, he can perform some action on behalf of the victim unnoticed.

The attacker does not need to access the cookie or session data (that should not be possible thanks to same origin policy of browsers). Attacker exploits the basic principles of HTTP communication.

The <img> tag is especially tricky. An attacker can specify arbitrary URL as src attribute. The browser connects to that URL and expects to get an image, if the result of URL is not an image, the browser simply displays image placeholder, but the action represented by that URL was already performed – therefore you should always use POST method for modifications of data. The <img> element can also be hidden to avoid attention.

Example

Suppose that you have an application which allows users to register and login. The user has to enter an email address during registration so your application can send him password-reset email when he forgets it. Your application also allows to stay logged-in even if the user was not working with your application for several days.

Suppose that there is a form which allows the user to change his registration email without any confirmation (like entering current password or confirming the change of email from email message sent to original email address).

The password reset functionality simply generates new password and sends it to the current email address registered to an account.

The form can look like this (it is a change in database so it uses the POST method):

<form action="/change-email" method="post">
    <input type="email" name="new-email">
    <br>
    <button type="submit">
        Change my email
    </button>
</form> 

When this form is submitted, the browser appends cookie header to the POST request with session ID which is used to identify the visitor. It is absolutely irrelevant whether the form was really submitted by a person or by JavaScript from entirely different site.

The attacker can also create an account in your application and study behaviour and HTML code of it. He can copy that form and just add this simple JavaScript and remove visible form elements:

<form action="http://www.your-app.com/change-email" method="post" name="CSRF">
    <input type="hidden" name="new-email" value="attackers@email-address.com">
</form>
<script type="text/javascript">
    //submit the form immediatelly
    document.forms['CSRF'].submit();
</script>

The attacker will probably generate unique email addresses because your application does not allow multiple accounts per one email.

Now he has to distribute this code to places where he can expect a lot of traffic (sites that are often visited by users of your application). He can exploit XSS vulnerability of other website or create his own website with such code and use spam email to attract visitors to it.

Once a user of your website visits a page with this automated form, the form submits which results in change of email address of visitor’s account (suppose that the visitor is also user of your application and he is logged-in at the time of the attack). The attacker can then use the password-reset functionality of your application to generate a new password and cut the user off his account. The generated password will obviously be delivered into attackers email account.

Who is to blame?

Unfortunately, the developer is:

  • You should not allow change of important email address without password confirmation.
  • You should have sent a message to confirm address change to original email and initiate the change after it is confirmed by secret token from that message.
  • You should not generate a new password when somebody enters an email but once the person clicks the link in email generated by the password change request.
  • You should have secured the form with a unique token.

Protection from CSRF

OWASP mentions two basic means to protect users from CSRF attacks. First one is to check origin of the request and the second is to generate unique tokens for each action.

The origin of the request is usually passed in the Referer HTTP header of a request. It tells the backend the URL of the resource which is responsible for firing current HTTP request. E.g.: when you visit page-a.com and click a link leading to page-b.com, the Referer header will be page-a.com. When page-b.com request an image from page-b.com/images/img.jpg, the referer will be page-b.com. When the Referer header does not match your domain, it is probably CSRF attack.

The token method is based on randomly generated large number which is stored in session data and has to be submitted along with form data. Because the attacker cannot extract such data from your website (unless your application allows to communicate with any host using AJAX requests – header Access-Control-Allow-Origin: *), and it is very hard to guess current token value, the request is not accepted.

Summary

It is better to use some framework with built-in CSRF protection or a library dedicated to this task than taking care of this all by yourself. There is a tutorial on CSRF protection for Slim framework.