This is a continuation to the previous article on Enforcing HTTPS.

While redirecting all non-secure requests to secure URLs is good, a man-in-the-middle can still hijack the connection before the redirect. And if the user types the address as company.com in the address bar, it will access the site insecurely every single time.

A bit about Strict Transport Security

You can skip this if you are familiar with HSTS.

HTTP Strict Transport Security (HSTS) fixes that problem somewhat. It tells the browser: "You shall only access this URL over a secure connection.". By submitting a Strict-Transport-Security header, the browser saves it and redirects itself to the HTTPS version without making an insecure call.

How does the header look like? Here are a few examples:

Strict-Transport-Security: max-age=31536000

Strict-Transport-Security: max-age=31536000; includeSubDomains

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

The max-age property names how many seconds the rule should be cached. In these examples it has been set to 1 year. Be careful what you set here, because any browser that gets this header will remember that rule until it expires, and won't allow the user to access it insecurely. So be sure that your site will be encrypted still 1 year in the future too.

By including includeSubDomains, you say that this rule should be applied to any and all subdomains of the current domain as well. Be really careful with this one. You must be absolutely sure that you will never have any insecure subdomains.

The preload property says that you approve this rule to be included in modern browsers' preload lists. Be even more careful with this one. If you include it, anyone can submit your domain to browser preload lists. The really nice thing is that it will effectively hard-code the rule to browsers. This eliminates even the first insecure request. If you want to submit your domain for preload, go to https://hstspreload.org/.

HSTS in ASP.NET Core

A really easy way to add HSTS to your ASP.NET Core project is to use my handy NuGet library (among with a few other security headers that I'll talk about later).

Just install Joonasw.AspNetCore.SecurityHeaders from NuGet, and add the following to your Startup.cs Configure function:

if (env.IsDevelopment() == false)
{
    app.UseHttpsEnforcement();
    app.UseHsts(new HstsOptions
    {
        Seconds = 30 * 24 * 60 * 60,
        IncludeSubDomains = false,
        Preload = false
    });
}

You'll also need to add using Joonasw.AspNetCore.SecurityHeaders; at the top of the file. You can see I'm also using the HTTPS enforcement middleware I wrote in the previous article. This is very much necessary because HSTS can only be applied on a secure connection.

It sets the max-age to 30 days, and does not include subdomains, and does not allow preloading. You can quite easily modify these settings.

There is a good reason it is only enabled outside development. I'm pretty sure you don't want to always access every single site on localhost over HTTPS. Did the mistake once, and learned how erase HSTS entries from Chrome...

If you run the app now and check your F12 tools, you can see the header included:

Strict-Transport-Security:max-age=2592000

If you then write the insecure URL to the address bar, you will see an Internal Redirect (HTTP 307) in the network log. There was no request, the browser redirected to the secure version by itself.