.NET Core Middleware – OWASP Headers Part 1

Jamie Taylor11 comments
NET Core Middleware - OWASP Headers Header ImageSource: https://unsplash.com/photos/1oEPX-f_nj8 Copyright 2017: Ben Garratt

Today’s header image was created by Ben Garratt at Unsplash

Caveat

Just a quick note before we begin.

A caveat before we begin?

Hi everyone, this article series was written with .NET Core 1.x in mind. With the .NET Core 2.0 RTM (release to manufacturers) being right around the corner, and there being some breaking changes in 2.0, it’s worth noting that this article was written specifically for .NET Core 1.x.

Once .NET Core 2.0 has been RTMd, I’ll release an updated version of this article series where I upgrade the middleware to .NET Core 2.0. If you’ve found yourself here in the future (after 2.0 was released), I’ll add a link to the .NET Core 2.0 specific articles.

Anyway, back to the article.

In a recent post, I talked through ASP.NET Core’s middleware pipeline and touched on a overview of how it works.

you can read the post here

But I didn’t got deep into how it all works, which I’ll fix in this post. As with when I talked through Digital OceanAppVeyor, and Azure

amongst other things

I’m going to work through an actual project in order to walk us though how the ASP.NET Core middleware pipeline works. I’ll also cover the steps required to create an ASP.NET Core middleware item and how to inject it into the pipeline.

Don’t worry, it’s nothing related to dwCheckApi.

not this time, anyway

ASP.NET Core middleware

As a quick reminder, ASP.NET Core middleware are classes which are added to the ASP.NET Core request pipeline, and all requests are passed through the pipeline in order to generate a response.

Microsoft describe them as:

Middleware is software that is assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline. Request delegates are used to build the request pipeline. The request delegates handle each HTTP request.

source: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware#what-is-middleware

OwaspHeaders.Core

This post will use my OwaspHeaders.Core project as an example of how to build ASP.NET Core middleware.

if you want to skip the content of the post and read through the source code, you can do so here

be aware though, that the source code much further ahead of this post and will more closely match the content of the next post in this series

With this post and the next one in the series, we’re going to build a middleware which inspects the HTTP Context of the request and injects OWASP recommended headers into the response. We’ll decide on which headers to include (and their options, where applicable) by reading a JSON configuration object. The headers will be added to the generated response and we’ll pass further processing off to the next item in the middlware pipeline.

OWASP

Before we continue, it might be worth talking a little about OWASP, who they are and what they do.

OWASP (Open Web Application Security Project) is a not for profit organisation which was founded in 2001. It’s main aim is to provide information, support (via online channels and local chapters) and code examples which will help to increase the security of all web-based applications.

most of the advice they give can be applied to desktop/mobile applications, too

Their advice and articles cover most of the popular programming languages used to build modern applications. They have recommendations for protecting your applications from penetration testing

and those recommendations are used as the basis for most pen testing applications

specific recommendations for .NET projects (which, in some aspects extends to .NET Core projects, too), and even a directory of know attack types.

If you’ve never heard of OWASP, I would recommend taking a look through their website and familiarising yourself with some of the stuff that they do and provide for free.

We’re going to use the OWASP Secure Headers Project as a basis for building OwaspHeaders.Core, so it will definitely be worth taking a look through that before we continue.

I’ll wait, whilst you do that

Before we move into making our own middleware, let’s take a look at how middleware are built up.

middleware Makeup

A basic ASP.NET Core middleware class takes the following form:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Abstractions;
namespace middlewareExample
{
public class MySuperAmazingMiddleware
{
private readonly RequestDelegate _next;
public MySuperAmazingMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
/// The main task of the middleware. This will be invoked whenever
/// the middleware fires
/// </summary>
/// <param name="HttpContext">
/// The <see cref="HttpContext" />
/// for the current request or response
/// </param>
/// <returns></returns>
public async Task Invoke(HttpContext httpContext)
{
// Do something here
// Call the next middleware in the chain
await _next.Invoke(httpContext);
}
}
}

This extremely useful example middleware contains a RequestDelegate which will point to the next item in the pipeline, or will be null if this middleware is added as the last item in the pipeline.

private readonly RequestDelegate _next;
public MySuperAmazingMiddleware(RequestDelegate next)
{
_next = next;
}

It also contains an async method called Invoke, which takes the HttpContext. This is the method where our middleware will do it’s thing.

/// <summary>
/// The main task of the middleware. This will be invoked whenever
/// the middleware fires
/// </summary>
/// <param name="HttpContext">
/// The <see cref="HttpContext" />
/// for the current request or response
/// </param>
/// <returns></returns>
public async Task Invoke(HttpContext httpContext)
{
// Do something here
// Call the next middleware in the chain
await _next.Invoke(httpContext);
}

When we write our middleware, we’ll do all of our processing inside of the Invoke method and we’ll work directly on the HttpContext object.

if it’s not obvious, the above middleware doesn’t do anything other than call the next middleware in the pipeline

Consuming middleware

We’ve seen this in almost every project that we’ve built together

whenever we’ve included the MVC middleware, for example

but here’s how we consume our example middleware:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using middlewareExample;
namespace MyMiddlewareExampleUsage
{
public class Startup
{
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMiddleware<MySuperAmazingMiddleware>();
app.UseMvc();
}
}
}

I’ve removed most of the unrelated code in the startup class, but I’ve highlighted the line where we add the example middleware to the pipeline.

Because we’ve added it between the logger and MVC middlewares, incoming requests will be processed with our example middleware before the MVC middleware.

I covered the ordering of middleware and how important it is, in the previous post

Now that we can see how to create and consume a middleware class, let’s build one.

Let’s Create Our middleware

As with all of our projects in the past, we’ll use the terminal to create our project directory layout, build and test our code.

you can use your operating system’s file explorer if you like; I just really dig the terminal

Directory Layout

Bring up the terminal and change into your code directory.

I use a “CodeProjects” directory, but you can use whatever you’d like

We want to create and change into a directory called “OwaspHeaders.Core”:

mkdir OwaspHeaders.Core
cd OwaspHeaders.Core

While we’re here, we’re going to create a global.json:

touch global.json

The global.json is a convention for .NET Core projects

you can read more about global.json in the Microsoft documentation for it 

and a convention that we should always stick to. As per this tweet from Brad Wilson:

global.json tl;dr

I had the chance to explain this at a .NET Core event I attended recently.

The short version is the different versions of the .NET Core SDKs can be installed side-by-side. For example, you could have versions 1.0.4 and 2.0.0-preview2-006497 installed

pro tip: the machine I used to create this blog post and the code behind it is set up like that.

but your code repo could target version 1.0.4 without any issues, as long as there is a global.json file present which targets that version.

I’ve published an earlier, more detailed explanation here

global.json content

We want to target an early, yet stable, version of .NET Core. So let’s target 1.0.4, which is the version that most of the code bases I’ve written have targeted so far.

don’t worry if you want to support the latest and greatest, just change the value for the version that you want

Add the following to the global.json:

{
"sdk": {
"version": "1.0.4"
}
}
New Class Library

Create a new class library, which targets .NET Standard 1.4 (for portability reasons) and is called “OwaspHeaders.Core”:

dotnet new class --name OwaspHeaders.Core --framework netstandard1.4

There are new switches here which we haven’t used before.

but they should be pretty self explanatory

As a side note: because I have preview 2 of .NET Core 2.0 installed, I could have targeted .NET Standard 2.0, but we want this middleware to be useable on older versions of .NET Core.

Opening the code directory with VS Code (or whichever IDE you prefer), you should see a similar project layout to the following:

OwaspHeaders.Core VSCode

Replace the contents of OwaspHeaders.Core.csproj with the following:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A .NET Core Middleware which adds the OWASP recommended HTTP headers for enhanced security.</Description>
<VersionPrefix>0.0.0.1</VersionPrefix>
<Authors>Jamie Taylor</Authors>
<AssemblyName>OwaspHeaders.Core</AssemblyName>
<TargetFramework>netstandard1.4</TargetFramework>
<PackageId>OwaspHeaders.Core</PackageId>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.1.*" />
</ItemGroup>
</Project>

feel free to add yourself to the Authors tag. But leave my name there too, though 😛

Then we need to restore packages:

dotnet restore
SecureHeadersMiddleware

We’re about to build the file which contains the magic of our middleware, but first we need to find a HTTP header to add.

Taking a look at the headers section of the OWASP Secure Headers Project page, we’ll use HTTP Strict Transport Security (HSTS), which is the first header listed.

even though you should only use this with HTTPS connections (so after the TLS handshake has happened and a secure connection has been established)

 We’ll also use the example values for the header that we’ll add to the response.

we’ll look into making this configurable later

This means that we’ll use 31,536,000 as the number of seconds which the browser should remember that our server must be contacted via HTTPS; we’ll also tell the client’s browser that we want to include subdomains.

in case you’re wondering 31,536,000 seconds is 365 days

Firstly, let’s rename the file “Class1.cs” to “SecureHeadersMiddleware.cs”. Then paste the following code into it:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Abstractions;
namespace OwaspHeaders.Core
{
/// <summary>
/// A middleware for injecting OWASP recommended headers into a
/// HTTP Request
/// </summary>
public class SecureHeadersMiddleware
{
private readonly RequestDelegate _next;
public SecureHeadersMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
/// The main task of the middleware. This will be invoked whenever
/// the middleware fires
/// </summary>
/// <param name="HttpContext">The <see cref="HttpContext" /> for the current request or response</param>
/// <returns></returns>
public async Task Invoke(HttpContext httpContext)
{
httpContext.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
// Call the next middleware in the chain
await _next.Invoke(httpContext);
}
}
}

This code should look very similar to what we talked through earlier.

in this section, that is

I’ve highlighted the code which will add our header to the HTTP Response object.

You can read about the HTTP Response object here

At this point, we’re done. So let’s build the project:

dotnet build

This will package up our Class Library and place it in the bin directory:

OwaspHeaders Code Bin Directory

So how do we consume it? We’ll need another project.

Example Project

Change back to the root directory for the codebase:

cd ..

We need to create some kind of example project to consume our middleware with, so let’s create a webapi project.

dotnet new webapi --name example --framework netcoreapp1.1

We’ll choose a webapi project because it’s pretty much ready to go as soon as dotnet new has finished processing.

Replace the contents of “example.csproj” with the following:

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>A .NET Core Middleware which adds the OWASP recommended HTTP headers for enhanced security.</Description>
<VersionPrefix>0.0.0.1</VersionPrefix>
<Authors>Jamie Taylor</Authors>
<AssemblyName>OwaspHeaders.Core.Example</AssemblyName>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PackageId>OwaspHeaders.Core.Example</PackageId>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
</ItemGroup>
</Project>

again, feel free to add yourself to the Authors list. But leave my name there, too 😛

Now we need to change into the example directory and restore packages:

cd example
dotnet restore
Adding A Reference To The middleware

We need our example project to refer to our OwaspHeaders.Core project, so let’s add a project reference to it:

dotnet add reference ../OwaspHeaders.Core/OwaspHeaders.Core.csproj

The above line assumes that you’re still in the example sub directory and that you haven’t moved the other directories around.

We’re telling the .NET Core CLI to add a reference to the csproj file found by traversing up a directory then going into the OwaspHeaders.Core directory.

Taking a look at the example.csproj file, the .NET Core CLI should have added the highlighted lines:

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>A .NET Core Middleware which adds the OWASP recommended HTTP headers for enhanced security.</Description>
<VersionPrefix>0.0.0.1</VersionPrefix>
<Authors>Jamie Taylor</Authors>
<AssemblyName>OwaspHeaders.Core.Example</AssemblyName>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PackageId>OwaspHeaders.Core.Example</PackageId>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OwaspHeaders.Core\OwaspHeaders.Core.csproj" />
</ItemGroup>
</Project>

Now that we’ve added a reference to our middleware, we need to restore packages again. This is because our middleware reference is, effectively, a package reference. Doing that, we’ll see a slightly different output from normal:

dotnet restore
Restoring packages for /CodeProjects/OwaspHeaders.Core/example/example.csproj...
Restoring packages for /CodeProjects/OwaspHeaders.Core/OwaspHeaders.Core/OwaspHeaders.Core.csproj...
Lock file has not changed. Skipping lock file write. Path: /CodeProjects/OwaspHeaders.Core/OwaspHeaders.Core/obj/project.assets.json
Restore completed in 313.58 ms for /CodeProjects/OwaspHeaders.Core/OwaspHeaders.Core/OwaspHeaders.Core.csproj.
Installing Microsoft.NETCore.Jit 1.1.0.
Installing Microsoft.DiaSymReader.Native 1.4.0.
Installing Microsoft.NETCore.Runtime.CoreCLR 1.1.0.
Installing Microsoft.NETCore.App 1.1.0.
Generating MSBuild file /CodeProjects/OwaspHeaders.Core/example/obj/example.csproj.nuget.g.props.
Writing lock file to disk. Path: /CodeProjects/OwaspHeaders.Core/example/obj/project.assets.json
Restore completed in 7.14 sec for /CodeProjects/OwaspHeaders.Core/example/example.csproj.
NuGet Config files used:
/CodeProjects/.nuget/NuGet/NuGet.Config
Feeds used:
https://api.nuget.org/v3/index.json
Installed:
4 package(s) to /CodeProjects/OwaspHeaders.Core/example/example.csproj

The highlighted lines show that the .NET CLI first restores the packages for then generates an MSBuild setup for the OwaspHeaders.Core project.

When we build the example project, we’ll see the .NET CLI run the MSBuild script for the OwaspHeaders.Core project before the example project is built:

dotnet build
Microsoft (R) Build Engine version 15.1.1012.6693
Copyright (C) Microsoft Corporation. All rights reserved.
OwaspHeaders.Core -> /CodeProjects/OwaspHeaders.Core/OwaspHeaders.Core/bin/Debug/netstandard1.4/OwaspHeaders.Core.dll
example -> /CodeProjects/OwaspHeaders.Core/example/bin/Debug/netcoreapp1.1/OwaspHeaders.Core.Example.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:03.61
Consuming the middleware

Now that we’ve created the middleware, added a reference to it in the csproj for our example, and restored it as a package, we’d better make use of it.

We’ll need to include our middleware in the startup class, otherwise it wont be included in the pipeline. Let’s do that first:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OwaspHeaders.Core;
namespace example
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMiddleware<SecureHeadersMiddleware>();
app.UseMvc();
}
}
}

I’ve highlighted the lines that you’ll have to add in order to inject our shiny new middleware. The first highlighted line should be obvious to those who are not new to C#

in case you are relatively new, we’re including a reference to the namespace that the SecureHeaders class is in

All of the magic is happening in the ConfigureServices method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMiddleware<SecureHeadersMiddleware>();
app.UseMvc();
}

Here we’re leveraging the UseMiddlewareExtensions class (which is found in the Microsoft.AspNetCore.Builder namespace) to place the SecureHeaders class into the middleware pipeline.

the documentation for this class is available here

Testing

Before we test the middleware, we’re going to make it easy on ourselves.

We could easily just run the application and head over to /api/values

which is the default webapi controller action that the webapi template ships with

but we’ll add our own controller to make things a little easier.

In the Controllers directory, add a file called HomeController.cs and paste the following code into it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace example.Controllers
{
[Route("/")]
public class HomeController : Controller
{
// GET
[HttpGet]
public IEnumerable<string> Get()
{
return HttpContext.Response.Headers.Select(h => h.ToString()).ToArray();
}
}
}

This Controller will respond to any GET requests for “/” (i.e. the address of the application). What we’ll do here is get the response headers from the current HTTP Context and return them as a string.

If you run the application:

dotnet build
dotnet run

then request the application on whichever port you’re given

I was given localhost:5001

then you should get something similar to this:

Response Headers Generated

We only have the Strict-Transport-Security header here because we inspected the headers before the WebApi controller had finished processing it (i.e. before ASP.NET Core MVC has added any headers to the response).

“But this could be faked,” I hear you cry. So how then do we test that the headers are added correctly?

If you pull up your browser’s network inspector and refresh the page, you should see something similar to this:

Response Headers Test
Pssst… check the section labelled “Response Headers”

We can see here that our response headers were injected along with the others that we expect to find in a .NET Core WebApi controller action.

Where Do We Go From Here?

There are a few things we can do.

Firstly, we could improve the header injection code by only including headers if they’re not there. If we somehow injected the Strict-Transport-Security header before our middleware ran, we’d get an ArgumentException.

this is because the headers property is a Dictionary type, and you can’t have multiple instances of the same key in a Dictionary

The quickest way to check whether we already have the header before injecting it would be to change our Invoke method to match the following:

public async Task Invoke(HttpContext httpContext)
{
if (!httpContext.Response.Headers.ContainsKey("Strict-Transport-Security"))
{
httpContext.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
}
// Call the next middleware in the chain
await _next.Invoke(httpContext);
}

Secondly, we could read the configuration for our middleware from a configuration file. That way we wouldn’t have to re-build and re-release the middleware if we wanted to change any of the header values.

I’ll show you have that’s done… but in another post.

talk about teasing.

Although, you can check the github repo to get a heads up if you really wanted

Conclusion

We’ve recapped how the ASP.NET Core middleware pipeline processes requests and generates responses. We’ve also see what’s involved in creating and consuming a custom middleware class.

Have you created any custom middleware? If so, what did you create?

If not, what’s stopping you from creating middleware?

If this post has helped you out, please consider   Buy me a coffeeBuying me a coffee
Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)