Introduction
Castle Windsor is an easy way to implement Inversion Of Control. Full implementation documentation can be found at
Castle Project. The second step of implementing Castle Windsor is defining the Controller factory, especially overriding GetControllerInstance(). As documented here
Windsor tutorial part two (the code is pasted below for reference) it is only a couple of lines.
- protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
- {
- if (controllerType == null)
- {
- throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
- }
- return (IController)kernel.Resolve(controllerType);
- }
The override checks controllerType and returns an HTTPexcetpion if it is null. Otherwise ControllerType is resolved and the appropriate controller is returned. Once an application implements the preceding override, all the requests are routed here, the controller is resolved and returned. controllerType is null when a request has an invalid path. The article above also talks about 404 issues from favicons and how to handle them in routing. There were other suggestions on net that indicate to return null or return basecontroller itself to handle a null ContollerType.
Here is the modified version of the preceding code returning a base controller when controllerType is null.
- protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
- {
- if (controllerType == null)
- {
- return base.GetControllerInstance(requestContext, controllerType);
- }
- var controller = (IController)this.container.Resolve(controllerType);
- if (controller != null && controller is BaseController)
- {
- ((BaseController)controller).MefContainer = this._MefContainer;
- }
- return controller;
- }
For reference, here is the routing config that routes the requests to a controller:
- routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });
- routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");
- routes.IgnoreRoute("{robotstxt}", new { robotstxt = @"(.*/)?robots.txt(/.*)?" });
- ute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Account", action = "UserLogin", area = "", id = UrlParameter.Optional },
- namespaces: new[] { "Porzio.Its.WebRole.Controllers" }
- );
- routes.MapRoute(
- name: "404-PageNotFound",
- url: "{*url}",
- defaults: new { controller = "Errors", action = "Error" }
- );
So far so good, but what about when the user types in an incorrect URL or there is an attack on the website? Here is a snapshot of an ELMAH log from an application that was returning a basecontroller if controllerType is null. As you can see, the entire log is filled with IController erros.
This was actually someone trying to determine the vulnerabilities of the web application by scanning various URL patterns. All these invalid URLs will throw the controller for a path was not found or does not implement IController error. If the code was changed to return null, the error was a null controller returned. In either case, the log continues to grow, annoying and actual errors that must be fixed taht will get buried among the long irrelevant errors. This is one side of the problem. Another problem is the CPU is utilized to resolve a controller that doesn't exist and numerous such requests will drive up CPU utilization compared to the same volume of valid requests.
To handle this, the application could actually return a valid page like a login page or a dummy page and avoid cluttering the log and also decrease the pressure on the CPU to resolve the controller that doesn't exist. The following code is returning the login page for an invalid URL path. This can be changed to return a dummy blank page.
- protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
- {
- if (controllerType == null)
- {
- requestContext.HttpContext.RewritePath("/");
- controllerType = GetControllerType(requestContext, "Account");
- requestContext.RouteData.Values["controller"] = "Account";
- requestContext.RouteData.Values["action"] = "UserLogin";
- requestContext.RouteData.Values["area"] = "";
- return base.GetControllerInstance(requestContext, controllerType);
- }
- var controller = (IController)this.container.Resolve(controllerType);
- if (controller != null && controller is BaseController)
- {
- ((BaseController)controller).MefContainer = this._MefContainer;
- }
- return controller;
- }
The code is replacing the request path to the base URL and generates a valid controllerType. Then overwrite the controller action and area to the default routing or dummy page route. The last step generates a controller instance with a modified request and controllerType to return the login page. Now the login page is returned whenever there is an incorrect URL typed in by the user or someone tries to look for vulnerabilities by repeatedly scanning the URL of the web application.
Note: The blocking of an attacker IP is necessary but outsied the scope of this article. Also an attacker can change the source IP and again the log would become filled with errors and will increase CPU usage. Interestingly we saw attacks stopped after implementing the preceding solution. It could be either an attack stopped or an attacker's logic stopped the loop when a valid response is received and tries injecting something on that page.