2015-11-10

When you research web application security you will come across Cross-Site
Request Forgery (CSRF). This attack vector is taking advantage of cookies, but
in a preventable way. In this post we’ll discuss what the attack is and how it
can be prevented. We’ll also discuss Angular’s XSRF feature, which helps you
prevent attack. It requires cooperation from your server, and we’ll explain what
you need to do.

Note: Angular uses the acronym XSRF, but this is synonymous with CSRF.

What is Cross-Site Request Forgery (CSRF)?

Most attacks focus on stealing your cookies because nearly every website uses
cookies as a form of authentication. The setup is this: when a user logs into
your server, you set a cookie in the browser. This cookie contains a unique ID
which is a link to the user’s session information in your database. The browser
supplies this cookie on future requests, and the server knows who you are.

On the surface this sounds not-so-bad, but here is the catch: the web browser
can be tricked into making requests to your server, even if the end-user
didn’t perform the action themselves.

How is that possible? This attack exploits the default nature of the
HTML parser in your browser. Imagine that you run a store at www.mystore.com.
Your store has an API that allows users to make 1-click purchases. That API
uses a URL that requires the user to be logged in (to have a cookie session),
and looks like this:

Now imagine that there is a malicious person that wants to exploit your Buy API
and cause a headache for your users. This malicious person carries out their
attack by making posts on websites and social media that contain an image link
that looks like this:

Now imagine a customer who is logged into your site, who is visiting one of the
pages where this link has been placed. When their browser encounters this tag it
will automatically makes a GET request to the URL (because it naively wants to
show the picture to your customer). When it does this it automatically sends
along the cookies for www.mystore.com with the request. Thus, the server
thinks your customer is logged in and completes the 1-click purchase for the
customer!

This is pretty disturbing, as the customer has no idea that this has happened –
they are simply surfing the web as normal and aren’t aware that this request has
been made.

CSRF Mitigations That Don’t Work

Before we get into the proper solutions for this problem, I want to enumerate
two common approaches that don’t actually solve the problem. I mention these
because I see these solutions on the interwebs, but they are wrong:

Using POST Requests. It is sometimes thought that using proper form-based
POST requests will mitigate this attack, but that is not true. You can create
a form on another website and point its action URL to your site, and the
browser will do the same thing: make the malicious request, with cookies.

Using HTTP-Only or Secure cookies. While you definitely should use these
flags on your session cookie, they don’t implicitly stop the attack: the
browser still sends the cookies to your domain when a request is made to your
domain. Your server does not know if this is a real user or an attack.

With that out of the way, let’s move on to the important discussion: how do
we stop this for reals :)

How To Prevent CSRF

Preventing this attack requires a knowledge of where the request is coming from
in the browser. We want to ensure that the request was triggered by an explicit
action from a user who is using the JavaScript application on our website.

We can achieve this by relying on a set of rules that browsers respect, called
the Same-Origin Policy. This policy asserts that certain sensitive
operations are performed by JavaScript code that is running on our website, not
some other website.

One of those operations is the ability to read cookies. While the browser will
automatically supply your cookies for the domain of the request, there is one
useful limitation: the JavaScript code that is running on a website cannot read
the cookies of other websites. We can leverage this to create our CSRF
solution.

Using a CSRF Token Cookie

Because JavaScript can only read cookies from our domain, we will store a second
cookie on our domain that contains a unique token. This is the CSRF Token
cookie. This cookie must be created when the user is logged in, and should contain
a random, un-guessable string that is associated with the user’s session ID (for
future lookups).

Every time the JavaScript application wants to make a request, it will need to
read this token and send it along in a custom HTTP header. Because these
operations (reading the cookie, setting the header) can only be done on the same
domain of the JavaScript application, we can know that this is being done by a
real user who is using our JavaScript application.

A passive image tag or malicious form post, on another site, would not be able
to do these things. If your server sees a request that is missing the custom
header, or the token in the header is not the one that is associated with the
user’s session, your server should reject the request.

Leveraging Angular’s XSRF Feature

Angular packages the CSRF token approach, making it simpler for us to implement.
For every request that your Angular application makes of your server, the
Angular $http service will do these things automatically:

Look for a cookie named XSRF-TOKEN on the current domain.

If that cookie is found, it reads the value and adds it to the request as
the X-XSRF-TOKEN header.

Thus the client-side implementation is handled for you, automatically! But this
does leave the server side pieces in your hands. You will need to do the
following parts:

During login: create the CSRF token (with a random, un-guessable string), and
associate it with the user session. You will need to send it on the login
response as the XSRF-TOKEN cookie.

Assert that all incoming requests to your API have the X-XSRF-TOKEN header,
and that the value of the header is the token that is associated with the
user’s session.

That’s it! With a little bit of backend work, you now have a strategy which
protects you from CSRF attacks.

Go Forth and Secure All the Things!

My goal for this post is to demystify what CSRF is, and how Angular tries to
help you with a solution. CSRF is just a small piece in the web-application
security puzzle. For more information, please see the other posts that I have
written on this subject:

Token Based Authentication for Single Page Apps (SPAs)

Build Secure User Interfaces Using JSON Web Tokens (JWTs)

-r out

Show more