Why Strict Transport Security Header?

  • Requests HTTP are not encrypted and they are vulnerable since information is plain-text.
  • Typically, the user’s first request happens over HTTP, user rarely types “https://”.
    • Browsers may by default try HTTPS, but often user access the website over a link that was provided and that link may be over HTTP.
    • Normally, if the server recieves an HTTP connection, it will do a redirection (301) and will tell the client to connect over HTTPS.
  • Everything over HTTPS is protected, however, that very first connection that happen over HTTP is a possible attack vector for an attacker.
  • HSTS work against SSL Stripping Attacks, where an attacker intercepts that very first HTTP connection from the client, makes that same request but over HTTPS to the server.
    • Client makes HTTP request to the server.
    • Attacker in the middle intercepts this requests.
    • Attacker sends this very same request to the server but over HTTPS.
    • Server replies normally to this HTTPS requests,without actually knowing that this is a on-path attack (man-in-the-middle). Let’s just assume the the server is asking for credentials.
    • The attacker takes the response, decrypts it (because actual conversation over HTTPS is happening between attacker and server) and sends this response to the actual client over HTTP.
    • Client receives page. As far as the client is concern, it asked for an HTTP website and it received an HTTP website.
    • Client provides credentials. These credentials are sent over HTTP to the attacker.
    • The attacker can either use or record those credentials.
  • This is a diagram illustrating concept above (trying my best with diagrams lol):


What Is Strict-Transport-Security

The Strict-Transport-Security HTTP response header field (STS header field) indicates to a UA that it MUST enforce the HSTS Policy in regards to the host emitting the response message containing this header field.

  • So, it is a response header that instructs the Browser to always visit the site over HTTPS even though the user’s request was over HTTP.
    • If working appropiately, if the user manually typed “http://”, the Browser will make an internal redirection (307 Internal Redirect) to the HTTPS version of the website.
    • As it is an internal redirect, this never went out to the internet.
  • Example:
    • Browsed to “http://medium.com”.
    • Browser did an internal redirect to the HTTPS version of the website.
    • Browser issued a new requests to “https://medium.com”.

Directives Of HSTS

max-age (required)

  • The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS.
    • A max-age value of zero (i.e., “max-age=0”) signals the UA to cease regarding the host as a Known HSTS Host, including the includeSubDomains directive (if asserted for that HSTS Host).

includeSubdomains [optional]

  • This rule will apply to all of the site’s subdomains as well.

preload [optional]

  • There is one tiny thing that we have not yet discussed…
    • The Browser will only honor the HSTS header if it was received over HTTPS.
    • If the server sent the HSTS header over HTTP, the Browser will simply ignore it.
    • Meaning that, in order for HSTS to take effect, the client must visit the HTTPS version of the site at least once.
  • This is why HSTS is typically paired by the server having a HTTP to HTTPS redirection rule.
    • To enforce users, when typing “http://” to be redirected to “https://”, where the server is configured to sends HSTS header and now the Browser will honor and apply header.
  • However, we still have that risk of that initial connection when the client browse to the HTTP version of the website…
  • That HTTP request represents an attack vector.
  • In order to solve this and for the Browser to never allow an HTTP connection to a site, Browsers come with a in-built locally preload static list of hosts that will always connect over HTTPS.
  • When sending this directtive you are actually applying to be included in the Browser’s static list, provided you have the other specified requirements:
    • Serve a valid certificate.
    • Server all subdomains over HTTPS.
    • Redirect from HTTP to HTTPS.
    • Send STS header with these values “max-age=31536000; includeSubDomains; preload”
      • max-age at least 31536000.
  • Note that inclusion can take a while.
  • Note that if you want to be excluded, this also can take several months. So, you really need to make sure all parts of your organization are ready for this change.

  • HSTS Submission

  • Example of header:
    Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
    
  • On a last note: HSTS also disables ability to click-through TLS warnings.

HSTS Browsers Static-List

Firefox List

Chromium-Based Browser List

Important Considerations From RFC Itself

UAs need to prevent users from “clicking through” security warnings. Halting connection attempts in the face of secure transport exceptions is acceptable. See also Section 12.1 (“No User Recourse”).

Disallow “mixed security context” loads (see Section 2.3.1.3).

An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport.

The order of appearance of directives is not significant.

UAs MUST ignore any STS header field containing directives, or other header field value data, that does not conform to the syntax defined in this specification.

If an STS header field contains directive(s) not recognized by the UA, the UA MUST ignore the unrecognized directives, and if the STS header field otherwise satisfies the above requirements(1 through 4), the UA MUST process the recognized directives.

If an HSTS Host receives an HTTP request message over a non-securetransport, it SHOULD send an HTTP response message containing astatus code indicating a permanent redirect, such as status code 301(Section 10.3.2 of [RFC2616]), and a Location header field valuecontaining either the HTTP request’s original Effective Request URI(see Section 9 (“Constructing an Effective Request URI”)) altered asnecessary to have a URI scheme of “https”, or a URI generatedaccording to local policy with a URI scheme of “https”.

An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport.

The UA MUST replace the URI scheme with “https” [RFC2818], and if the URI contains an explicit port component of “80”, then the UA MUST convert the port component to be “443”, or if the URI contains an explicit port component that is not equal to “80”, the port component value MUST be preserved; otherwise, if the URI does not contain an explicit port component, the UA MUST NOT add one. NOTE: These steps ensure that the HSTS Policy applies to HTTP over any TCP port of an HSTS Host. NOTE: In the case where an explicit port is provided (and to a lesser extent with subdomains), it is reasonably likely that there is actually an HTTP (i.e., non-secure) server running on the specified port and that an HTTPS request will thus fail (see item 6 in Appendix A (“Design Decision Notes”)).

  • Some attempts of diagrams trying to illustrate above concepts:

  • HTTPS and HSTS header:

  • HTTP & HSTS header:

  • URI & port combination

  • Again, do not take above diagrams too seriously as they are just my attempt to picture what HSTS RFC says…

Scenarios

  • As for these example, I wanted to try out “Deno”, open-source JavaScript runtime.
  • Don’t take the code that seriously, since it is just an example and I am not a professional developer.

  • For below two examples server environment is:
    • HTTP version of site hosted over port 80.
    • HTTPS version of site hosted over port 443.

Example 1: Connection Successful

  • Browser received HSTS header from “https://sitea.net” with “max-age: 120”.
  • Browser trusts certificate.
  • User manually visits “http://sitea.net” within the “max-age” value.
  • Browser does an internal redirect (307) to the HTTPS version of site.
  • Connection successful.

Example 2: HSTS Not Applied

  • Browser received HSTS header from “https://sitea.net” with “max-age: 120”.
  • Browser trusts certificate.
  • User waits more than 120 seconds and then manually visits “http://sitea.net”.
  • Browser can access “http://sitea.net”.
  • Connection successful BUT over HTTP.

  • For below two examples server environment is:
    • HTTP version of site hosted over port 8080.
    • HTTPS version of site hosted over port 443.

Example 3: Connection Fails

  • Browser received HSTS header from “https://sitea.net” with “max-age: 120”.
  • Browser trusts certificate.
  • User manually browse the HTTP version of the site “http://sitea.net:8080” within the value specified in “max-age”.
  • Browser makes an internal redirect (307) to “https://sitea.net:8080”.
  • Connection fails because there has never been any HTTPS service over port 8080.

Example 4: Connection Successful

  • Browser received HSTS header from “https://sitea.net” with “max-age: 120”.
  • Browser trusts certificate.
  • User manually browse the HTTP version of the site “http://sitea.net” within the value specified in “max-age”.
  • Browser makes an internal redirect (307) to “https://sitea.net”.
  • Connection successful.

  • For below example server environment is:
    • HTTP version of site hosted over port 80.
    • HTTPS version of site hosted over port 4443.

Example 5: Connection Fails

  • Browser received HSTS header from “https://sitea.net:4443” with “max-age: 120”.
  • Browser trusts certificate.
  • User manually browse the HTTP version of the site “http://sitea.net” within the value specified in “max-age”.
  • Browser makes an internal redirect (307) to “https://sitea.net”.
  • Connection fails as there is nothing listenting on port 443, HTTPS site is on port 4443.

  • For below example server environment is:
    • HTTP version of site hosted over port 80.
    • HTTPS version of site hosted over port 443.

Example 6: HSTS Not Applied

  • Browser received HSTS header from “https://siteb.net” with “max-age: 120”.
  • Browser DOES NOT trust certificate.
  • User manually browse the HTTP version of the site “http://siteb.net” within the value specified in “max-age”.
  • We accessed “http://siteb.net”.
  • As certificate is not trusted, HSTS does not get apply.

In Summary

  • HSTS is a response header that instructs the Browser to only visit the HTTPS version of the site for the next “x” seconds.
    • Where “x” is the value provided in directive “max-age”.
  • Still with HSTS, you are still vulnerable to the very first HTTP request. In order to achieve this you can apply to get included in the preload static list that Browsers have.
  • Before applying to be included please do your researh to understand if your whole organization is ready for this change and all domains and subdomain are available over HTTPS and with valid certificates.
  • HSTS must be sent over HTTPS, otherwise the Browser will ignore it.
  • If TLS certificate is not valid for any reason, HSTS will not get apply.
  • HSTS does not support IPv4 or IPv6 addressess.
  • You really need to understand on which ports you are deploying your HTTP and HTTPS sites, as depending on how you have set them up, connections might fail, and this is “by-design.”
    • If you use standard HTTP and HTTPS ports, then you are good to go.
    • If you use a non-standard HTTPS website, say port 4443,
      • http://site.com:4443 => https://site.com:4443. Successful given that there is in fact an HTTPS site on port 4443.
      • http://site.com => https://site.com. Fails because HTTPS site is hosted on port 4443, NOT 443.
      • http://site.com:8080 => https://site.com:8080. Fails because HTTPS site is hosted on port 4443.

As A Site Note…

  • If you are interesting in this environment that I have setup.

  • Generated self-signed certificates with “openssl” with the following commands:

# Generate private key 
openssl genrsa -out sitea.net.key.pem 2048

# Generate CSR with previous private key
openssl req -new -key sitea.net.key.pem -out sitea.net.csr.pem -nodes

# Generate self-signed certificate

openssl x509 -req -in sitea.net.csr.pem -out sitea.net.pem -key sitea.net.key.pem -extfile v3.ext -days 365
  • Where “v3.txt” file was modified according to “sitea.net” and “siteb.net” for each certificate:
➜  https cat certs/v3.ext                   
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints       = CA:TRUE
keyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign
subjectAltName         = DNS:siteb.net, DNS:www.siteb.net
issuerAltName          = issuer:copy
  • To force Google Chrome to trust self-signed I had to import the public key of certificates into “chrome://settings/certificates?search=cert”.

  • To make “deno” bind to port 443 I had to use following command to allow non-root users to bind. Otherwise, I got a OS permission error.

sudo setcap cap_net_bind_service=+ep "/home/USER/.deno/bin/deno"
  • Code for deno server:
function myResponse(req) {
  const response = new Response("<html> <h1>My Site </h1> </html>", {
    status: 200,
    headers: {
      "content-type": "text/html",
      "Strict-Transport-Security": "max-age=120",
    },
  });
  return response;
}

// Starting HTTP & HTTPS servers for siteA.net
Deno.serve({ port: 80 }, myResponse);

Deno.serve({
  port: 443,
  cert: Deno.readTextFileSync("./certs/sitea.net/sitea.net.pem"),
  key: Deno.readTextFileSync("./certs/sitea.net/sitea.net.key.pem"),
}, myResponse);

// Starting HTTP & HTTPS servers for siteB.net
Deno.serve({ port: 8080 }, myResponse);

Deno.serve({
  port: 4443,
  cert: Deno.readTextFileSync("./certs/siteb.net/siteb.net.pem"),
  key: Deno.readTextFileSync("./certs/siteb.net/siteb.net.key.pem"),
}, myResponse);

Resources