.net core, Security

Creating a RESTful Web API template in .NET Core 1.1 – Part #4: Securing a service against XSS, clickjacking and drive-by downloads

Back in October 2015, I posted a short article with some information on HTTP response headers which, if configured correctly in IIS, can help protect your site.

I decided it was time to update this article for .NET Core 1.1 and Kestrel, with how to protect against:

What does SecurityHeaders.io say about the default Web API service?

I created a RESTful Web API service using the default project template in Visual Studio 2015, and uploaded it to Azure. I decided to use Scott Helme‘s SecurityHeaders.io site to check if there are any problems. Looks like there’s a few…

screenshot-1482610473

The site helpfully gives a few more detail on specific problems.

screenshot-1482610498

screenshot-1482612327

Obviously the default project template serves no realistic purpose, and it’s not really fair to criticise a template intended as a programming starting point – all I’m doing here is charting a journey from a poor rating on SecurityHeaders.io to a better one.

Start with the SecurityHeaders NuGet package

So we can start fixing these problems by adding some headers – and with .NET Core, it’s really easy with the SecurityHeaders NuGet package, written by Andrew Locke. This starts to give my websites and services protection with just a few lines of code.

I can install this package to the project in Visual Studio from the command line:

Install-Package NetEscapades.AspNetCore.SecurityHeaders

Then to add the three headers listed above, we change the Configure method in the web project’s Startup.cs file – simply add a collection of pre-defined policies, and apply these to the response.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    var policyCollection = new HeaderPolicyCollection()
        .AddFrameOptionsSameOrigin()    // prevent click-jacking
        .AddXssProtectionBlock()    // prevent cross-site scripting (XSS)
        .AddContentTypeOptionsNoSniff(); // prevent drive-by-downloads
        
    app.UseCustomHeadersMiddleware(policyCollection);
 
    // ...add other configuration here...
}

Finally, make sure the container uses the custom middleware by adding the line below to the “ConfigureServices” method in the Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCustomHeaders();
 
    // ...other services here...
}

Andrew has written a great post going into much more detail here and has open-sourced the code on GitHub here.

So now if I put my service into SecurityHeaders.io, I get a better result:

screenshot-1482612504

Reduce XSS risks further by adding a content security policy

These three headers are a good start – but there’s room for improvement. As the image above suggests, I cam improve things further using a Content-Security-Policy.

You can read lots more about a Content Security Policy here, but basically it adds an extra tier of protection against XSS for modern browsers by allowing you to specify what resources the site is allowed to load. The site here gives a good “starter policy” to use – shown below.

default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';

If I wanted to allow my site to use javascript resources from a CDN, I could modify this policy to specify the origin of the CDN – however, I’ll stick with the starter policy for now.

Again, I can use the SecurityHeaders NuGet package to help me out – there’s a useful extension method called “AddCustomHeader” that I can use. I can modify the code above with just one extra line to add a content-security-policy.

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            var policyCollection = new HeaderPolicyCollection()
                .AddFrameOptionsSameOrigin()    // prevent click-jacking
                .AddXssProtectionBlock()    // prevent cross-site scripting (XSS)
                .AddContentTypeOptionsNoSniff() // prevent drive-by-downloads
                .AddCustomHeader("Content-Security-Policy""default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';"); // https://content-security-policy.com/

So let’s look now at how SecurityHeaders.io rates our service:

screenshot-1482613274

Looking pretty good!

And for bonus marks

We’ve covered the major bases – but there were a couple of warnings about information being leaked by the server, namely the server type and the framework.

screenshot-1482613563

How do we get rid of these?

Unfortunately the SecurityHeaders NuGet package can’t help us here – these headers are added too late in the middleware pipeline for us to intercept. Fortunately we have a couple of extra tricks up our sleeve to get rid of these.

Remove the Server header

.NET Core has a built in feature to remove this – we just modify the settings for the Kestrel web host in our application’s Program.cs file. I’ve highlighted the line we need in the code snippet below.

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel(options => options.AddServerHeader = false)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();
 
        host.Run();
    }
}

Remove the X-Powered-By header

This is a slightly strange one – we can’t remove this header in our C# code. This header appears to be added by IIS in Azure. Fortunately we can remove this by changing our web.config file and adding the node, as shown in the snippet below:

  <system.webServer>
    <!-- ... other web.config stuff goes here... -->
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" /> <!-- required because this is added by IIS - https://github.com/aspnet/Hosting/issues/571 -->
      </customHeaders>
    </httpProtocol>
  </system.webServer>

So after deploying this, we can check what SecurityHeaders.io says now:

screenshot-1482614545

Still an A+ and this time, no more informational warnings.

You might still use IIS as a reverse proxy in front of a set of web-servers hosting Kestrel, and obviously you’ll still need to add these headers to IIS as described here.

Double checking

I thought it might be a good idea to check this against more than one site that benchmarks the security of your sites headers – another useful one is from High-Tech Bridge who provide a free header checking service. I got an A from this service – so a bit less than from SecurityHeaders.io, because I’m on HTTP rather than HTTPS, and therefore cookie dropped in by Azure isn’t secured. Obviously moving to HTTPS would solve that problem.

I found some interesting statistics on High-Tech Bridge’s site – I’ve taken a screen-capture, where they claim that only 2.4% of sites get an A grade for the security of their sites headers. A staggering 85.3% don’t have even this basic level of security. It’s amazing that just a few lines of .NET code will get you into the top 2.4%.

screenshot-1482784444

Wrapping up

You can see that with just a few extra lines of .NET code, you can add significant and valuable extra protection to your site. I personally prefer doing as much as possible of this in my C# code, and the SecurityHeaders NuGet package written by Andrew Locke helps a lot. You can check the security of your own site’s headers using the SecurityHeaders.io site written by Scott Helme. I think these headers are an important addition to my Web API and App templates, as it bakes in elements of security to the code from the earliest stages in a project.