| 5 min read
Disclaimer : This is not "How to code" tutorial. This article is written with the consideration that visitor have knowledge of ASP.NET Web API.
Well, we all know the need of security in Web Applications be it a front end or back-end (API) of the web app. The web world is full of trespassers. HTTP Protocol is designed in plain text which leads to vulnerability. Your web application can be exposed in many many ways.
Let's talk about the WebAPI, I assume you are developing RESTful Service which will be consumed by applications targeted on platforms (e.g. Windows 8 application, Web application, Android or iOS application). There will be vast scope of vulnerability since the service is being used by various end points.
This article explains just another way of implementing security. There are many other ways to achieve secure Web API (including built in authorization modules in ASP.Net and IIS).
Microsoft is well known for the documentation they publish. ASP.Net documentation is one of the best documentations ever I have referred. One should always learn Pipeline / Lifecycle before actually starting development. This helps to understand how exactly the mechanism works. Luckily, we have a very good and self descriptive poster that explains the Lifecycle of HTTP Message in ASP.NET Web API... You can grab it from here : ASP.NET Web API Poster
(Image courtesy : DotNetCurry)
I will not explain the lifecycle in this article. You can refer following articles / posters if you want to learn lifecycle in more depth.
Message Handlers are actually middleware of the whole architecture outline and the very first stage of the request pipeline. The only work message handler does is to process HTTP requests on the way in and HTTP response messages on way out. Message Handler is just a class that inherits HttpMessageHandler which is an abstract class.
Since the role of message handler is only to process HTTP requests that are incoming and outgoing, we can say message handler is the "Gateway of Web API". And as you know, guarding the gateway of kingdom is the best way to keep the insiders safe.
Web API is so flexible that it can be customized at any level. Fortunately we can write custom handlers that allows us to work on all the incoming and outgoing HTTP requests and responses. There can be several usecases of writing custom handlers. Custom message handlers are usually used for logging request and response errors, adding custom headers in requests etc.
Configuring Custom Message Handlers is as easy as adding a single line in WebAPIConfig.cs file present in App_Start
config.MessageHandlers.Add(new CustomMessageHandler() { InnerHandler = new HttpControllerHandler(config)});
Alternative would be
CustomMessageHandler customMessageHandler = new CustomMessageHandler(){ InnerHandler = new HttpControllerHandler(config)}; | |
config.Routes.MapHttpRoute( | |
name: "DefaultApi", | |
routeTemplate: "api/{controller}/{id}", | |
defaults: new { id = RouteParameter.Optional }, | |
constraints : null, | |
handler : customMessageHandler | |
); |
We create response when we don't find any token or the token is invalid. Usually, we can not bypass the pipeline unless we create an instace of HttpControllerDispatcher . We create a new HttpControllerHandler instance with the Route config and set it to InnerHandler. More information about InnerHandler is very well explained in this blog post : ByteRot.
Custom handlers derive from DelegatingHandler and they are basically overriding SendAsync method. To find appropriate signature of method in detail, please refer ASP.Net documentation. I am just giving example snippet here.
public class CustomMessageHandler: DelegatingHandler | |
{ | |
protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) //Standard signature | |
{ | |
const string tokenName = "Auth-Token"; | |
if (request.Headers.Contains(tokenName)) //Check if request header contains auth token or not. | |
{ | |
string requestToken= request.Headers.GetValues(tokenName).First(); //get the first of Auth token from request header | |
try | |
{ | |
//VALIDATE THE TOKEN.. E.G. DECRYPT THE TOKEN AND CHECK IF THE USER IS VALID OR NOT | |
//I WILL BE SHARING EXAMPLE SNIPPET SOON ON MY GITHUB ON VARIOUS APPROACHES OF ACHIEVING SECURITY | |
//SUCH AS BASE64 ENCRYPTION, X.509 ENCRYPTION ETC. | |
if(//USER IS INVALID) | |
{ | |
HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid username or password / identity."); | |
return Task.FromResult(reply); | |
} | |
} | |
catch (Exception ex) //token not found or invalid token | |
{ | |
HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid token."); | |
return Task.FromResult(reply); | |
} | |
} | |
else // IF REQUEST DOES NOT HAVE AUTHENTICATION TOKEN | |
{ | |
HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Ooops, can not find token, make sure the requests have token."); | |
return Task.FromResult(reply); | |
} | |
return base.SendAsync(request, cancellationToken); | |
} | |
} |
I have explained most of the code in inline comments. Basically it checks if every incoming request does have the authentication token or not. If request doesn't have token, handler returns response with status code "unauthorized". If the incoming request has authentication token, it checks whether token is valid or not.
I will be sharing some of the encryption techniques soon on my GitHub.
What this approach does :
Inspect every request and check if authentication token exist. If exist, check if it is valid.
Without validating token, user won't be able to see the data since we are bypassing the whole pipeline for invalid tokens / missing tokens.
Only requests with valid token pass through the pipeline and process further. This ensures the data is secure enough. When is this approach helpful?
When you are trying to consume API from pure HTML / JS application.
When you are trying to consume API from Mobile application.
Combine as many as possible parameters together (username, password, IP address) and encrypt it while logging in the user... And later use the secure encrypted token as validator of API.. This will make sure that the request is coming from the same device which user used to authenticate (Log In)..
Feel free to comment and help me to improve the article!
Regards Rahul.