This post is about a new feature / code restructure in Angular v4.3+ that is pretty well documented, yet, hardly talked about in posts, and worth drawing special attention to.
The feature we are talking about here is Angular helpers for guarding against XSRF attacks in its old and new HTTP modules.
What Is XSRF? (Cross Site Request Forgery)
XSRF (Also knwon as CSRF) is a form of web security attack that can allow others to impersonate your identify and perform some actions on websites that you are already logged in to. Let’s start with an over simplified version of how the attack and protection work (as I understand them).
- You login to website A and get a session cookie, maybe use it a bit
- You open website B in a new tab or even the same tab, you are still logged into website A
- The owner of website B (or someone who hacked it) happens to be a user of website A too, and knows what calls it sends to the server to perform different tasks
- Website B loads a hidden form with the submit URL going to website A
. It could be sending an email on your behalf, deleting many important items, whatever that is you can perform in website A
- Website A takes the request and performs it, because you are still logged in
There are many ways to protect against this attack. Mainly you’d use server-generated token stored in somewhere that’s not a cookie.
A classic server side application with a single form each page that submits back to the server can generate a token as a hidden field in the form, and then validate it on submit, and generate a new token for the next page etc. Website B will not have a way to generate a valid token when trying to simulate the server form.
A client side application (SPA) could do the same with headers, sending the token in a header when making any API request (A.K.A AJAX call), and getting the new token in the response of that API / AJAX call.
The initial token may be obtained via a call to a known API, or in a cookie that’s downloaded when the HTML page is loaded. Since the other site can only do only put a form on its own page, it cannot modify headers to inject the token, or call the API directly due to CORS (Cross Original Resource Sharing) policy.
Angular HTTP APIs & XSRF Protection
By default the Angular v2+
Http service (from
HttpModule) had builtin support for the Cookies To Headers XSRF protection technique we just mentioned.
If it detects a cookie with the name
XSRF-TOKEN, it adds an HTTP header named
X-XSRF-TOKEN with the same value to the next HTTP request you make.
Since the support is so transparent, I have seen developers who did not even realise it’s there. If you are a front end developer who had someone else on the backend implement the server issuing and validation of the tokens, you may not even realise it’s there.
But things changed slightly in Angular v4.3, that you probably need to be aware of.
In Angular v4.3, a new library for creating HTTP (AJAX) requests was added to Angular.
Instead of injecting an
Http object from
@angular/http as you did since v2.0, you inject an
@angular/common/http. The new
HttpClient has several new features but most notably:
- Automatically maps responses to JSON by default (configurable)
- Added a pipeline for injecting middleware to requests/responses (called interceptors, like those of AngularJS v1.x)
- Has some really cool testing utilities that are much simpler than the old v2.0
Whether you like it or not, you’ll probably end up using the new
HttpClient in your Angular applications. This is because starting Angular v5.0, the old
Http will be deprecated (so it may be removed entirely in v6).
For the record, the relatively quiet introduction of the new module in v4.3 and the deprecation of the old in v5 still makes me feel anxious regardless of how good the new one is. The old one generally just worked, and the way it’s replaced reminds me of the 3 router versions that were written during Angular v2.0 pre-releases.
Since the new
HttpClient came with proper support for request and response interceptors, it made total sense for the XSRF support in it to be implemented as interceptors as wel, not as part of
This is all cool and good design, but there is a catch, which although is well documented, it’s not having enough people talking about on Medium and Twitter etc.
The XSRF in
HttpClient is an opt-in feature. It’s NOT enabled by default, unlike the
Http service. This means that you could easily migrate your code from
HttpClient and not realise that your builtin XSRF support has been lost, until you test the application and you start getting
403 Unauthorized errors from your API of course.
Luckily, all you need to do to get the support back is just import another module, like:
HttpModule supported sending the XSRF-TOKEN header for all HTTP requests. While the new
HttpClientXsrfModule made sure it doesn’t send the header for GET or HEAD requests.
GET requests don’t make much sense for XSRF attacks anyway because the attack is about changing data while impersonating you (the attacker cannot get the result anyway because of CORS rules). GET requests by definition should not change data.
However, if your API relies on this, your app might break when you move to
HttpClient. In that case you might want to look at this Github issue I created and apply the workaround github.com/angular/angular/issues/19885
HttpClientXsrfModule also makes it easy to change the cookie & HTTP header keys separately if you require that.
Of course you have to remember that both XSRF protection and JSONP require server support as well from the API server you are calling for them to function.
Finally, the same support is also included as opt-in for JSONP requests as well, by adding
HttpClientJsonpModule (also from
@angular/common/http) to your
imports. This utilizes the power of interceptors that allows them to avoid sending an HTTP request completely and do something completely different instead.
As I said, the usage of HttpClient in general, as well XSRF support (and what is XSRF in general) is well documented by the Angular team.
Check it out at https://angular.io/guide/http.
I still noticed that not enough people are talking about this feature and the actual change, so, hopefully this post can bring some awareness to it.