Request Filtering in ASP.NET Core: Part 1 - Overview

Sep 17, 2016     Viewed 8662 times    2 Comments
Posted in #Request Filtering 

This is the first post of a series in Request Filtering in ASP.NET Core. In this post I will try to give an overview about Request Filtering, how to use it and why we should care about it.

What is Request Filtering?

Request Filtering is the process of scanning the incoming HTTP requests on the server side and applying some sort of rules that you set up to filter those requests before they served to the clients. The Request Filtering is very helpful to prevent harmful requests from reaching the server.

For the Internet Information Services (IIS) administrators the Request Filtering is a built-in security feature that was introduced in IIS 7.0, and replaces much of the functionality that was available through the UrlScan add-on for IIS 6.0.

Use Request Filtering

UrlScan is a security tool was provided as an add-on to earlier versions of IIS, so administrators could enforce tighter security policies on their Web servers. Within IIS 7 and above, all the core features of UrlScan have been incorporated into a module called Request Filtering, and a Hidden Segments feature has been added.

Filter based on File Extensions

This feature defines a set of allowed file extensions that IIS serves.

<configuration>
 <system.webServer>
  <security>
   <requestFiltering>
    <fileExtensions allowUnlisted="true" >
     <add fileExtension=".psd" allowed="false"/>
    </fileExtensions>
   </requestFiltering>
  </security>
 </system.webServer>
</configuration>

Filter by Verbs

This feature defines a list of verbs that IIS accept as part of a request.

<configuration>
 <system.webServer>
  <secntinue,
    StopFilters
}

Also may we need an options for the request filtering to access all the registered filters.

public class RequestFilteringOptions
{
    public IList Filters { get; } = new List();
}

After that need more three classes one is an extensions for the RequestFilteringOptions and two required for the middleware.

public static class RequestFilteringOptionsExtensions
{
    public static RequestFilteringOptions AddRequestFilter(this RequestFilteringOptions requestFilteringOptions, IRequestFilter filter)
    {
        if (filter == null)
        {
            throw new ArgumentNullException(nameof(filter));
        }

        requestFilteringOptions.Filters.Add(filter);
        return requestFilteringOptions;
    }
}
public class RequestFilteringMiddleware
{
    private readonly RequestDelegate _next;
    private readonly RequestFilteringOptions _options;

    public RequestFilteringMiddleware(
        RequestDelegate next,
        RequestFilteringOptions options)
    {
        if (next == null)
        {
            throw new ArgumentNullException(nameof(next));
        }

        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }

        _next = next;
        _options = options;
    }

    public Task Invoke(HttpContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var requestFilteringContext = new RequestFilteringContext
        {
            HttpContext = context,
            Result = RequestFilteringResult.Continue
        };

        foreach (var filter in _options.Filters)
        {
            filter.ApplyFilter(requestFilteringContext);

            switch (requestFilteringContext.Result)
            {
                case RequestFilteringResult.Continue:
                    break;
                case RequestFilteringResult.StopFilters:
                    return Task.FromResult(0);
                default:
                    throw new ArgumentOutOfRangeException($"Invalid filter termination {requestFilteringContext.Result}");
            }
         }

         return _next(context);
    }
}
public static class RequestFilteringMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestFiltering(this IApplicationBuilder app, RequestFilteringOptions options)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }

        return app.UseMiddleware(options);
    }
}

The middleware classes is straightforward for those who writing ASP.NET middleware before, the idea for our middleware is simple, iterate over all the request filtering that have been registered an calling the ApplyFilter which execute the actual filter, after that we gathering the result from each filter and check the Result property in the context to know if we need to go further to the next filter, or the actual filter is applied and no need to keep filtering.

Implementation APIs

Now we are ready to implement the basic request filtering techniques that are available in the IIS. FYI I will dig only into the essentials classes for each type.

Filter based on File Extensions

As we saw in the previous post that file extensions filter have AllowUnlisted, FileExtension and Allowed properties, so we can use our abstraction to define a set of options as the following:

public class FileExtensionsOptions : IRequestFilterOptions
{
     public bool AllowUnlisted { get; set; } = true;

     public IList FileExtensionsCollection { get; set; } = new List();

    

Twitter Facebook Google + LinkedIn


2 Comments

Matthew (12/14/2016 11:38:16 PM)

You are telling us about ASP.NET Core and then "implement" filtering in IIS XML config... ARE YOU KIDDING???

Andy Mehalick (12/25/2016 2:51:36 AM)

Yeah I'm confused, where is ASP.NET Core in any of this?


Leave a Comment