This article shows how to add headers in a HTTPS response for an ASP.NET Core MVC application. The HTTP headers help protect against some of the attacks which can be executed against a website. securityheaders.io is used to test and validate the HTTP headers as well as F12 in the browser. NWebSec is used to add most of the HTTP headers which improve security for the MVC application. Thanks to Scott Helme for creating securityheaders.io, and André N. Klingsheim for creating NWebSec.
2021-04-07 Add link to https://csp-evaluator.withgoogle.com
2020-12-11 Updated to .NET 5
2019-10-06 Updated to .NET Core 3.0
2018-11-10 Updated to .NET Core 2.2, small corrections
2018-05-07 Updated to .NET Core 2.1 preview 2, new Identity Views, 2FA Authenticator, IHttpClientFactory, bootstrap 4.1.0
2018-02-09 Updated, added feedback from different sources, removing extra headers, add form actions to the CSP configuration, adding info about CAA.
A simple ASP.NET Core MVC application was created and deployed to Azure. securityheaders.io can be used to validate the headers in the application. The deployed application used in this post can be found here: https://webhybridclient20180206091626.azurewebsites.net/status/test
Testing the default application using securityheaders.io gives the following results with some room for improvement.
Fixing this in ASP.NET Core is pretty easy due to NWebSec. Add the NuGet package to the project.
<PackageReference Include="NWebsec.AspNetCore.Middleware" Version="3.0.0" />
Or using the NuGet Package Manager in Visual Studio
All the NWebSec code configurations are added to the Startup.cs class in the Configure method.
Add the Strict-Transport-Security Header
By using HSTS, you can force that all communication is done using HTTPS. If you want to force HTTPS on the first request from the browser, you can use the HSTS preload: https://hstspreload.appspot.com
app.UseHsts(hsts => hsts.MaxAge(365).IncludeSubdomains());
Add the X-Content-Type-Options Header
The X-Content-Type-Options can be set to no-sniff to prevent content sniffing.
Add the Referrer Policy Header
This allows us to restrict the amount of information being passed on to other sites when referring to other sites. This is set to no referrer.
app.UseReferrerPolicy(opts => opts.NoReferrer());
Scott Helme write a really good post on this:
Add the X-XSS-Protection Header
The HTTP X-XSS-Protection response header is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. (Text copied from here)
app.UseXXssProtection(options => options.EnabledWithBlockMode());
Add the X-Frame-Options Header
You can use the X-frame-options Header to block iframes and prevent click jacking attacks.
app.UseXfo(options => options.Deny());
Add the Content-Security-Policy Header
Content Security Policy can be used to prevent all sort of attacks, XSS, click-jacking attacks, or prevent mixed mode (HTTPS and HTTP). The following configuration works for ASP.NET Core MVC applications, the mixed mode is activated, styles can be read from unsafe inline, due to the razor controls, or tag helpers, and everything can only be loaded from the same origin.
app.UseCsp(opts => opts.BlockAllMixedContent() .StyleSources(s => s.Self()) .StyleSources(s => s.UnsafeInline()) .FontSources(s => s.Self()) .FormActions(s => s.Self()) .FrameAncestors(s => s.Self()) .ImageSources(s => s.Self()) .ScriptSources(s => s.Self()) );
Due to this CSP configuration, the public CDNs need to be removed from the MVC application which are per default included in the dotnet template for an ASP.NET Core MVC application.
NWebSec configuration in the Startup
//Registered before static files to always set header app.UseHsts(hsts => hsts.MaxAge(365).IncludeSubdomains()); app.UseXContentTypeOptions(); app.UseReferrerPolicy(opts => opts.NoReferrer()); app.UseXXssProtection(options => options.EnabledWithBlockMode()); app.UseXfo(options => options.Deny()); app.UseCsp(opts => opts .BlockAllMixedContent() .StyleSources(s => s.Self()) .StyleSources(s => s.UnsafeInline()) .FontSources(s => s.Self()) .FormActions(s => s.Self()) .FrameAncestors(s => s.Self()) .ImageSources(imageSrc => imageSrc.Self()) .ImageSources(imageSrc => imageSrc.CustomSources("data:")) .ScriptSources(s => s.Self()) );
When the application is tested again, things look much better.
Or view the headers in the browser, for example F12 in Chrome, and then the network view:
Here’s the securityheaders.io test results for this demo.
Removing the extra infomation from the Headers
You could also remove the extra information from the HTTPS headers, for example X-Powered-By, or Server, so that less information is sent to the client.
Remove the server headers from the kestrel server, by using the UseKestrel extension method.
.UseKestrel(c => c.AddServerHeader = false)
Add a web.config to your project with the following settings:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <httpRuntime enableVersionHeader="false"/> </system.web> <system.webServer> <security> <requestFiltering removeServerHeader="true" /> </security> <httpProtocol> <customHeaders> <remove name="X-Powered-By"/> </customHeaders> </httpProtocol> </system.webServer> </configuration>
Now by viewing the response in the browser, you can see some unrequired headers have been removed.
Further steps in hardening the application:
You can fix your domain to a selected amount of authorities. You can control the authorities which can issue the certs for your domain. This reduces the risk, that another cert authority produces a cert for your domain to a different person. This can be checked here:
Or configured here:
Then add it to the hosting provider.
Use a WAF
You could also add a WAF, for example to only expose public URLs and not private ones, or protect against DDoS attacks.
The certificate should also be tested and validated.
https://www.ssllabs.com is a good test tool.
Here’s the result for the cert used in the demo project.
I would be grateful for feedback, or suggestions to improve this.