IP Filtering in ASP.NET Web API

One of the functionalities I had to use fairly often on different ASP.NET Web API projects that I was involved in in the past was IP filtering – restricting access to the whole API, or to parts of it, based on the caller’s IP address.

I thought it might be useful to share this here. More after the jump.


Whenever you build a functionality like that, there are two roads you might wanna take:
– as a whitelist – meaning deny the majority of the callers, and only let some predefined ones through
– as a blacklist – meaning allow the majority of the callers, and only block some predefines ones

While you could approach the task from many different angles, for the purpose of this post, let’s assume you want to have it configurable from the web.config file, and that you will use a whitelist approach (reject everyone, unless they are in the config).

Configuration in ASP.NET is far from the friendliest (at least until we can get ASP.NET Core), and there is some ugly configuration code we will have to write – to deal with configuration elements, sections and so on. We will be extending the types from System.Configuration to provide a reasonably friendly user experience.

We could probably achieve the same with just appSettings but having a dedicated configuration section would be a bit more elegant for the end user.

So let’s imagine we will want to have configuration like this:

To achieve this, here are our nasty ASP.NET configuration components. First the section:

Next, the addresses collections:

And finally the individual entries:

Implementation of IP filtering

Once we got the configuration out of the way, the usage can take four forms:

  • a message handler (wrapping your entire Web API)
  • a filter (applied on a specific action only)
  • an HttpRequestMessage extension methods (so that it can be called anywhere, i.e. inside a controller)
  • and, as a bonus, an OWIN middleware (wrapping your entire OWIN pipeline, if you are using it)

We should actually start with the extension method, as it’s going to be the base for everything – in the other components (except for OWIN middleware), we will just call that.

The extension method is shown below:

So in the extension method, we check if the request is local, and if it isn’t we will proceed to grabbing the IP address from the request.

Then we consult our configuration and if the caller’s address is found in our configuration and check whether the IP should be allowed or not. This check could be done in a more elaborate way (see for example here) – but for our use case it’s enough to just compare them in a simple way, without working about stuff like IP ranges.

We made use of another extension method in the above snippet – (request.GetClientIpAddress()) – I blogged about it a while back and it has also been added to WebApiContrib but for the record, here it is:

So now we can proceed towards building our individual components, as they will all rely on the extension method that we created.

First a filter:

And now a handler:

We could make an OWIN middleware too. For this we will need a new extension method – working directly off the OWIN dictionary and grabbing the IP information from there.

For Project Katana purposes, we can make the middleware Microsoft specific, and as such we could build the extension method off IOwinContext instead of the raw OWIN dictionary (since Web API on OWIN relies on Katana anyway).

That code is shown below, and is quite similar to the HttpRequestMessage extension code we wrote a bit earlier.

And finally, let’s add the middleware itself (again – Katana specific middleware to be precise):

And that’s it! You can now use the components on:

  • individual actions or controller (the filter)
  • on the whole API (the message handler)
  • on the whole OWIN pipeline (the middleware)
  • or whereever you see fit (the Request extension method)

Of course you can make it much better and more robust, add support for submasks and such, add extra dynamic configuration, instead of static IP look up from web.config and so on – but hopefully this will be a good starting point.

All the source code for this article is at Github.