Tim DeschryverTim

Watch out what you expose with Angular Interceptors

The Angular logo
@tim_deschryver

You're probably already using Angular Interceptors. Even if you can't find one in the application you're working in, there is a big chance that one of the added libraries makes use of an interceptor, especially if you're dealing with Authentication headers.

You can compare an Angular Interceptor with the entrance door of your favorite conference. When you arrive at the conference you need go to through the entrance, and you can pick up some swag before entering the venue. After the day is over and you want to exit the venue, you need to pass through the door again and you might pick up some more swag.

Going back to Angular interceptors, all HTTP requests that are initiated through the HTTP client are handled by the registered interceptors right before they're sent to the server, and again when the server answers with a response. This makes interceptors the ideal place to put common logic to work with HTTP traffic.

Interceptors come in different flavors, and they're often responsible for a specific task. For example, you can write an interceptor that acts as a caching layer, an interceptor that's responsible for showing and hiding a loader indicator, or another one to handle HTTP errors unanimously. But, the most popular interceptor is the one that adds request headers to the HTTP request, for example adding an Authentication header to the request headers with the token of the user.

It's about these interceptors that I want to talk to you about. A typical interceptor that does this is often implemented like one of the below.

From the Angular docs:

From an article that comes as one of the first google results:

From the top Stack Overflow question and the corresponding answer:

Do you notice what might go wrong here?

Let me give you a hint, what if the application sends an HTTP request to a 3rd party API?

Correct, then all the additional headers that are added by the interceptor are also sent to the 3rd party API. Ideally, this shouldn't be happening, but when it does - by accident or not - you don't want to leak (sensitive) information to a 3rd party.

There are several solutions to prevent this from happening. Let's take a look at them.

The easiest, and most secure, is verifying the host of the outgoing request. Only when it's a trusted host, the headers are appended to the request.

When we apply this to the example of the Angular docs, the interceptor now looks like this.

A variant of the above solution is more flexible and uses a configurable collection of host names.

Because practice makes the check configurable, many libraries use this technique. For example, the Angular Auth OIDC Client uses configurable secure routes to only append the Authorization header to requests that are sent to the configured host names (secure routes).

Another solution uses the HttpContext to ignore specific requests. But, this makes it also easy to forget to add the context to a request, which is not ideal.

This looks as follows.

First define a HttpContextToken.

Then set the token for a request.

Lastly, verify if the token is set in the interceptor.

Conclusion

All HTTP requests that are initiated by the HTTP client are intercepted by your Angular interceptors. This is a powerful feature that allows you to add logic to all outgoing HTTP requests.

But, this makes it also easy to leak (sensitive) information to 3rd party APIs. These requests are also intercepted by the same interceptors. This way you can accidentally add more information to the request that you would want, even without knowing it. The most common violation is adding an Authorization header to the requests.

As a best practice, always verify if the outgoing request is sent to a trusted host before appending headers to the request.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo
Support the blog Share on Twitter Discuss on Twitter Edit on GitHub