Should I return an HSTS header for 404 error pages?

Suppose attacker tricks victim to click on link to your site, browser uses http, attacker in strong network position intercepts and sends malicious content and makes victim believe content came from your site.

How would HSTS help? If the victim visited your site before, their browser has an HSTS "flag" for the domain, and clicking the link would make the browser use https instead, foiling the attack. But only if the victim has visited the site before!

What about victims who did not visit the site before, or visited the site too long ago? This is what HSTS Preload is for. I recommend adding your site to HSTS Preload list. This would foil the attack regardless of presence of HSTS header on 404 error page.

Note, HSTS on 404 error page doesn't affect the attack as described. Whether the link provided by attacker leads to 404 or not doesn't matter - the attacker can intercept and replace the response either way. What matters is whether this browser has visited your site before (within the time limit) or not, and whether you have HSTS Preload (and updated browser) or not.

With nginx webserver, there is a way to add HSTS response header to error pages too, the optional "always" argument to "add_header". I don't know how to do that with IIS or whether it's even possible with IIS, but maybe it is. In your place, I would fix this, but I don't see how it's a security problem.


First retrieving arbitrary non-existing resources like /test.xml isn't within normal operation of your site, and therefore the problem doesn't exist anymore after the browser has seen the HSTS header somewhere (or preloaded it), as it causes upgrade to HTTPS on hostname level (and domain level with includeSubDomains).

Also, it doesn't make a huge difference whether an attacker uses URLs that would lead to 404 responses instead of 200, as if there's no cached HSTS the attack would work similarly with both. Complaining about this test failing demonstrates lack of understanding how HSTS works and what's it for.

That said, making your web server add the HSTS header instead of the application makes the probability of seeing the header a bit higher on a few rare border cases like this, while HSTS preloading would handle this while it also helps those who have never visited the site before.

HSTS Preloading

As you already have includeSubDomains, you should be ready for preloading:

  1. Add preload to the header:

    Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    
  2. Submit your site for preloading at https://hstspreload.org/


Adding the HSTS header with Microsoft IIS

Since IIS 10.0 1709 there has been native support for HSTS, meaning you could use <hsts>:

<site name="Contoso" id="1">
    . . .
    <hsts enabled="true" max-age="31536000" 
          includeSubDomains="true" preload="true" 
          redirectHttpToHttps="true" />
</site>

Before that, you had to

  • do the HTTP to HTTPS redirection separately

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <httpRedirect enabled="true" 
                          destination="https://contoso.com" 
                          httpResponseStatus="Permanent" />
        </system.webServer>
    </configuration>
    
  • add the header using <customHeaders>:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <httpProtocol>
                <customHeaders>
                    <add name="Strict-Transport-Security" 
                         value="max-age=31536000; includeSubDomains; preload" />
                </customHeaders>
            </httpProtocol>
        </system.webServer>
    </configuration>
    

As it's Microsoft Windows, there are GUI equivalents for both, here in the same picture:

HSTS on IIS GUI