CSRF – Cross Site Request Forgery

There are a few security concepts every developer should understand and be able to implement before they are trusted with sensitive data. Things like SQL injection, XSS, salting and a whole bunch of other things can really cause you and your users a lot of trouble.
Maybe i’ll write something about those some other time, but for now i’m singling out CSRF which is fairly easy to exploit and can have some pretty fantastic results yet it seems, from my experience anyway, to get less attention than the other attacks I’ve mentioned.

So, what is it?

In short, CSRF is when an attacker is able to submit a request from the victim’s browser to a site that they are authenticated with. Wikipedia puts it like this:

Cross-site request forgery, also known as a one-click attack or session riding and abbreviated as CSRF (sometimes pronounced sea-surf[1]) or XSRF, is a type of malicious exploit of a website whereby unauthorized commands are transmitted from a user that the website trusts.[2] Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user’s browser.

For example, if you log in to your banks website and someone tricks you in to clicking a link that looked something like this:

myfakeinsecurebank.com/?transfer-to=badguy&amount=200&from=savings

If myfakeinsecurebank.com is careless, and they probably are with a URI like that, then you might have just been duped in to sending badguy $200 dollars!
The website can’t tell if you are responsible for this request, all it knows is that it came from your browser and you are logged in.
Well, you shouldn’t put things you shouldn’t link to in the get string!” you might be thinking to yourself and you’d have a point, but that really doesn’t prevent CSRF. The attacker could just make a website (this is where the “Cross Site” part comes from) that sends a post request or even a series of post requests to the target website to do all sorts of things.

If you are logged in to a site and that site isn’t actively trying to prevent CSRF then the attacker can take advantage of that and submit requests to the site through your browser as if they were you.

Quick Example

Lets see it in action! Here is some pretty insecure/quick code you might find on a site like myfakeinsecurebank.com:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!--?php
session_start();
$namespace = 'csrftest';
$username = 'user';
$password = 'pass';

if (isset($_POST['logout']) || !isset($_SESSION[$namespace])) {
    $_SESSION[$namespace] = array();
}

if (isset($_POST['username'])
    && isset($_POST['password'])
    && $_POST['username'] == $username
    && $_POST['password'] == $password) {

    // Loging the user "in" by storing the username
    $_SESSION[$namespace]['username'] = $username;
}

if (isset($_SESSION[$namespace]['username'])) {

    if (isset($_POST['name'])) {
        $_SESSION[$namespace]['name'] = $_POST['name'];
    }

    $message = isset($_SESSION[$namespace]['name'])
        ? 'Hello ' . $_SESSION[$namespace]['name'] . '!'
        : 'Set your name below!';
?-->
1
<code lang="php">

1
<code lang="php">


1
<code lang="php">

1
<code lang="php">



1
<code lang="php">


Hopefully its simple enough that a quick glance will give you the general idea. You log in with the hard-coded username and password and you are presented with a screen where you can give yourself some kind of a name.
Here it is in action:
http://mjump.kodingen.com/SS/CSRF/target.php

1
<code lang="php">

Maybe, perhaps, one day you are minding your own business setting your name to various clever things and decide to hit up Google for some inspiration. A particular site stands out, almost too good to be true, and you click the link.
http://serialsignal.com/csrf/attack.php

1
<code lang="php">

Maybe a bit of doubt comes over you as you think about clicking that link and maybe you don’t just click something with “csrf” and “attack” in the title; its just an example right? Its unlikely anyone really wanting to take advantage of you would be so obvious but fortunately http://serialsignal.com/csrf/attack.php is harmless unless you don’t have a sense of humor.

1
<code lang="php">

Here is the code behind it:

1
2
3
4
5
<script><br />
function attack() {<br />
    document.forms['attack'].submit();<br />
}<br />
</script>
1
<code lang="php"><code lang="html">


1
<code lang="php"><code lang="html">

1
 

Visiting the link will do a pretty snappy javascript triggered post in to target.php changing your name to “Doofus”. If you have javascript enabled it will happen almost instantaneously. Clicking the button wont get you a free ipod, but if you have javascript disabled it might just entice you. As you can see it really doesn’t take much to exploit it and you can be assured serialsignal.com doesn’t share any session information with mjump.kodingen.com; they are entirely seperate sites, yet serialsignal was able to manipulate target.php through your logged in session anyway.

1
<code lang="php">

How to stop it?

1
<code lang="php">

There are several ways you can go about doing it but I really do suggest the “Synchronizer Token Pattern” if possible. Essentially you generate a unique (per login or request) token which is embedded in each sensitive form as a hidden input. The principle is that the attacker will not be able to guess this token so a request forwarded through you without the token can be rejected. Properly implemented this has not only the benefit of being almost impossible to spoof from an external request but it also makes it possible for your users to make sure you are protecting them by viewing the source code.

1
<code lang="php">

Here is a modified version of target.php that implements the most basic form of this pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!--?php
session_start();
define('SESSION_NAMESPACE', 'csrftest');
$username = 'user';
$password = 'pass';

if ((isset($_POST['logout'])
        &&  csrfOk())
    || !isset($_SESSION[SESSION_NAMESPACE])) {

    $_SESSION[SESSION_NAMESPACE] = array();
}

if (isset($_POST['username'])
    && isset($_POST['password'])
    && $_POST['username'] == $username
    && $_POST['password'] == $password) {

    // Loging the user "in" by storing the username
    $_SESSION[SESSION_NAMESPACE]['username'] = $username;
    $_SESSION[SESSION_NAMESPACE]['csrf_token'] = hash("sha512",mt_rand(0,mt_getrandmax()));
}

if (isset($_SESSION[SESSION_NAMESPACE]['username'])) {

    if (isset($_POST['name'])) {
        if (csrfOk()) {
            $_SESSION[SESSION_NAMESPACE]['name'] = $_POST['name'];
        } else {
            $_POST['name'] = $_SESSION[SESSION_NAMESPACE]['name'];
            echo '

<h1--> WATCH WHAT LINKS YOU CLICK!
1
<code lang="php">

1
2
3
';
}
}
1
<code lang="php"><code lang="php">

$message = isset($_SESSION[SESSION_NAMESPACE][‘name’])
? ‘Hello ‘ . $_SESSION[SESSION_NAMESPACE][‘name’] . ‘!’
: ‘Set your name below!’;
?>

1
<code lang="php"><code lang="php">

1
<code lang="php"><code lang="php">



1
<code lang="php"><code lang="php">


1
<code lang="php"><code lang="php">

1
<code lang="php"><code lang="php">



1
<code lang="php"><code lang="php">

1
<code lang="php"><code lang="php">

http://mjump.kodingen.com/SS/CSRF/target2.php
* Note: Before you check out target2.php log out of http://mjump.kodingen.com/SS/CSRF/target.php! They are on the same domain so if you are using the session from target.php there will be no token and target2.php will not let you do anything, even log out. So LOG OUT OF target.php! You don’t still want someone changing your name do you?

1
<code lang="php"><code lang="php">

I generate a token and toss it in the session every time the user logs in; it will be unique and unguessable. DO NOT JUST HASH THE USERNAME OR ANY OTHER STATIC VALUE! If the attacker can guess the token then there is no point and its very easy to create an account and try basic things like md5 or base64 on the username to reproduce the token they see on the forms.

1
<code lang="php"><code lang="php">

Here is the attack.php thats pointed to target2.php:
http://serialsignal.com/csrf/attack2.php

1
<code lang="php"><code lang="php">

Its EXACTLY the same as the first one but it posts to target2.php instead of target.php. You can see that this time the attack doesn’t work. attack2.php would have to first find some way of getting the csrf token in order to affect target2. This is a basic implementation that is effective enough but you can get even better security if you have a different token on each form. I may go over how I do that method at a later date but, for an idea of how it works, I highly suggest you check out at

1
<code lang="php"><code lang="php">

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Prevention_Frameworks

1
<code lang="php"><code lang="php">

They also have some good tips, such as logging out of sites when you are done with them because even if its not open in a window, if server thinks your machine is logged in and isn’t keeping track of you CSRF attacks against it can be exploited. Throw some ajax in the mix and the attackers can scan a wide range of vulnerable sites you haven’t logged out of for valid sessions.

1
<code lang="php"><code lang="php">

If you use Zend Framework 1.x I recommend you check out Zend_Element_Hash and if you use ZF2 then you can check out the CSRF element.

1
<code lang="php"><code lang="php">

I also recommend you spend a bit of time reading around the OWASP site, its a great reference.

1
<code lang="php">

Leave a Reply

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