asp.net webapi webapp sql

In this new post, I’ll show you how to add security header to our website to be ready for a penetration test. This is something that we don’t look at until it is too late and we receive the report from the penetration company.

The source code of this project is on GitHub. The demo website for this project is here.

How can we test our website?

First, there are a lot of tools we can use to have a first check of your website. I like to use Observatory by Mozilla. This is a online tool that helps to show the major issues with some recommendations.

Mozilla Observatory website - Add Security Header our website
Mozilla Observatory website

There is another useful website with the same functionality: Security Headers checks your website and gives you some more information, in particular, about new headers.

Security Header home page - Add Security Header our website
Security Header home page

In both cases, you have to type in the address only the domain without http or https.

Create a test website

Now, open your Visual Studio and you create an ASP.NET Core Web.

Create an ASP.NET Core Web App
Create an ASP.NET Core Web App

First, I deploy this basic project without any change on my online server. Now, I go to the Observatory from Mozilla and I run the test. After few seconds, I have this result and this is the screenshot.

ASP.NET Core website out of the box - Add Security Header our website
ASP.NET Core website out of the box

So, I think it is not great. For this reason, I have to add some security header to our website to improve its quality. Let’s start.

Security Headers

Here you have the list of the most important security headers and a brief explanation. For more information about each header, there is a link to the documentation.

Strict-Transport-Security

HTTP Strict Transport Security (HSTS) protect websites against man-in-the-middle attacks by indicating the browser to access the website using HTTPS instead of using HTTP. More info on the Mozilla website.

X-Frame-Options

The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame><iframe><embed> or <object>. Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites. More info on the Mozilla website.

X-Permitted-Cross-Domain-Policies

The X-Permitted-Cross-Domain-Policies HTTP response header can be used to indicate whether or not an Adobe products such as Adobe Reader should be allowed to render a page. Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other applications. More info on the Adobe website.

X-XSS-Protection

The HTTP X-XSS-Protection response header is a feature that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although these protections are largely unnecessary in modern browsers when sites implement a strong Content-Security-Policy that disables the use of inline JavaScript ('unsafe-inline'), they can still provide protections for users of older web browsers that don’t yet support CSP. More info on the Mozilla website.

X-Content-Type-Options

The X-Content-Type-Options response header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This is a way to opt-out of MIME type sniffing, or, in other words, to say that the MIME types are deliberately configured. More info on the Mozilla website.

Referrer-Policy

The Referrer-Policy header controls how much referrer information (sent via the Referer header) should be included with requests. This may prevent information disclosure as URLs may contain sensitive data. More info on the Mozilla website.

Feature-Policy

The Feature-Policy header provides a mechanism to allow and deny the use of browser features in its own frame, and in content within any <iframe> elements in the document. It can prevent the use of sensible APIs such as microphone, or it can help to fix performance issues such as using oversized images. More info on the Mozilla website.

Expect-CT

The Expect-CT header lets sites opt-in to reporting and/or enforcement of Certificate Transparency requirements, to prevent the use of mis-issued certificates for that site from going unnoticed. This helps detecting man-in-the-middle attacks by someone that could generate a certificate for your domain. Cloudflare has a service to monitor certificate generation: Introducing Certificate Transparency Monitoring. More info on the Mozilla website.

Content-Security-Policy

The Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks (XSS). More info on the Mozilla website.

The implementation

So, the project I created is build with NET6. That means, the configuration is in the Program.cs and there is not start up file. Also, I like to remove the header X-Powered-By introduced by Microsoft for IIS. If this header is present, the website has a low rating.

Add web.config

First, this is easy. We have to add to our project a web.config and add this XML:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<system.webServer>
		<httpProtocol>
			<customHeaders>
				<remove name="X-Powered-By" />
			</customHeaders>
		</httpProtocol>
		<security>
			<requestFiltering removeServerHeader="true" />
		</security>
	</system.webServer>
</configuration>

So, this configuration will remove the header. That’s it.

Update Program.cs

Now, I think it is more simple if I show you the entire code and then comment it.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios,
    // see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.Use((context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            MustRevalidate = true,
            NoCache = true,
            NoStore = true,
        };

    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("Content-Security-Policy",
        "default-src 'none'; " + "script-src 'self'; " +
        "connect-src 'self'; " +
        "img-src 'self'; " +
        "style-src 'self'; " +
        "base-uri 'self'; " +
        "form-action 'self'; " +
        "frame-ancestors 'none';");
    context.Response.Headers.Add("Referrer-Policy", "strict-origin");
    context.Response.Headers.Add("Permissions-Policy", "geolocation=(), microphone=()");

    return next.Invoke();
});

app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Now, we can see there are only few lines more than the Program.cs generated by Visual Studio. I highlighted the new lines. First, line 17 starts to create a middleware that it will be called before each request.

Then, I set the Cache-Control (line 20). Now, the interesting part between lines 27 and 37; I add the headers. In this example all files (images, fonts, style sheets…) are coming from the website domain.

Then, I use the function that ASP.NET Core provides for HTTPS redirection and HSTS (lines 42-43).

For a better score, we can change the HSTS because the default can create some issues. We can use this code after line 4:

builder.Services.AddHsts(options =>
{
    options.Preload = true;
    options.IncludeSubDomains = true;
    options.MaxAge = TimeSpan.FromDays(60);
    options.ExcludedHosts.Add("example.com");
    options.ExcludedHosts.Add("www.example.com");
});

The result

So, we added security header to our website to obtain a good score. What is the result? First, I run again the Observatory by Mozilla and after few seconds the result is really good. The website has A+. I want to point out the score 120/100!!!

The result from Mozilla Observatory
The result from Mozilla Observatory

Now, it is time to test the website also with Security Headers website. So, after few seconds, the result is still stunning: A+.

Scan with Security Headers
Scan with Security Headers

So, this is a very good job! Well done!

Wrap up

In conclusion, in this post Add Security Header our website, we learn how to improve the security of our website adding specific headers. So, browsers can render the pages in a more secure way than before and the communication is more protected.

By Enrico

My greatest passion is technology. I am interested in multiple fields and I have a lot of experience in software design and development. I started professional development when I was 6 years. Today I am a strong full-stack .NET developer (C#, Xamarin, Azure)

This site uses Akismet to reduce spam. Learn how your comment data is processed.