Let’s say we have built a restaurant review site
restaurant-reviews.com. Our application makes
POST requests to
/reviews (on the same domain) to create a review. The application has a standard authentication system that uses a cookie to identify the user.
Now let’s say an attacker decides to make a site at
<form action="https://restaurant-reviews.com/reviews" method="post"> <input type="hidden" name="review" value="SOME EVIL REVIEW"> </form>
An attacker gets a user of
restaurant-reviews.com to visit
my-restaurant-reviews.com. This can happen a number of ways, but maybe
restaurant-reviews.com allows users to embed HTML in reviews, so the attacker created a simple anchor tag that linked to
my-restaurant-reviews.com hoping someone would click on it.
When the page loads, the form will submit and the browser will send along that user’s cookies for
POST request to
restaurant-reviews.com/reviews will see that the user is logged in since the session cookie was sent along in the request and create the malicious review without the user knowing. This can happen just by clicking on a link to
In traditional server-rendered web apps, a common technique is to generate a CSRF “token” for each active user session managed by the application. The server would render this token in a hidden input in a form. When the form is submitted, this token would be sent back to the server and will be used to verify that the authenticated user is the one actually making the request to the application.
For example, in Laravel, we can create a form like this with the Blade templating engine:
<form method="post" action="/reviews"> @csrf ... </form>
@csrf Blade directive would generate the following HTML:
<form method="post" action="/reviews"> <input type="hidden" name="_token" value="sbYzRkYwVfDEb9CH2BuLcivgDng69GM9l80Ek5ux"> ... </form>
The backend would then check to make sure the value in
_token in the POST request matches what it generated. If they don’t match, then the application will throw an error.
Instead of using a traditional form submission, what if we wanted to make our web application leverage AJAX? How would we then protect against CSRF attacks? A common technique is to use the Double Submit Cookie pattern.
The Double Submit Cookie Pattern works by having the server write a cookie (let’s say it is called
In our example, the malicious site
my-restaurant-reviews.com would not be able to read the
CSRF-TOKEN cookie set by
restaurant-reviews.com and place it in a header, since cookies can only be read by the domain that created them, so the request would fail.
Angular does exactly this. According to their documentation:
HttpClienthas built-in support for the client-side half of this technique.
HttpClientsupports a common mechanism used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default XSRF-TOKEN, and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
Disclaimer: Any viewpoints and opinions expressed in this article are those of David Tang and do not reflect those of my employer or any of my colleagues.