Implementing HTTP Headers for Better Website Security

In this article I'm going to cover how to add additional levels of security to your website using a few basic practices that I'm pretty sure most web developers and designers never think about when they build a web site. Even banks often fail this (I just checked several).

Specifically we're going to look at the following HTTP headers:

  • Strict-Transport-Security
  • Content-Security-Policy
  • Public-Key-Pins
  • X-Frame-Options
  • X-XSS-Protection
  • X-Content-Type-Options

Each of these headers have various security benefits that I'll cover today!

Strict-Transport-Security

HTTP Strict Transport Security is an excellent feature to support on your site and strengthens your implementation of TLS by getting the User Agent to enforce the use of HTTPS. This mitigates variants of man in the middle (MiTM) attacks where TLS can be stripped out of communications with a server, leaving a user vulnerable to further risk.

Here is a basic policy to enforce TLS on all assets and prevent mixed content warnings.

NginX: add_header Strict-Transport-Security "max-age=31536000; includeSubdomains" always;

Apache: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

For IIS, you add the header with Name = Strict-Transport-Security and Value = max-age=31536000; includeSubDomains.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="Strict-Transport-Security" content="max-age=31536000; includeSubDomains">

Content-Security-Policy

Content Security Policy is an effective measure to protect your site from XSS attacks. By whitelisting sources of approved content, you can prevent the browser from loading malicious assets. For example if a hacker places a malicious comment on a Wordpress blog or added it into some vulnerable javascript.

Here is a basic policy to enforce TLS on all assets and prevent mixed content warnings.

NginX: add_header Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'" always;

Apache: Header always set Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'"

For IIS, you add the header with Name = Content-Security-Policy and Value = default-src https: data: 'unsafe-inline' 'unsafe-eval'.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="Content-Security-Policy" content="default-src https: data: 'unsafe-inline' 'unsafe-eval'">

For my blog, as I'm using AWS S3 to serve this I need to use the meta tag equivalent.

Public-Key-Pins

HTTP Public Key Pinning protects your site from MiTM attacks using rogue X.509 certificates. By whitelisting only the identities that the browser should trust, your users are protected in the event a certificate authority is compromised.

First you'll need to get the fingerprint of your current certificate:

openssl x509 -pubkey < tls.crt | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

Where tls.crt is your public certificate.

Before you get started, just a note that you might want to use the "Public-Key-Pins-Report-Only" header to test.

NginX: add_header Public-Key-Pins "pin-sha256=''; includeSubdomains; max-age=2592000" always;

Apache: Header always set Public-Key-Pins "pin-sha256=''; includeSubdomains; max-age=2592000"

For IIS, you add the header with Name = Public-Key-Pins and Value = pin-sha256=''; includeSubdomains; max-age=2592000.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="Public-Key-Pins" content="pin-sha256=''; includeSubdomains; max-age=2592000">

Just as a note, you'll want to configure multiple pin entries so that you retain access in an emergency.

X-Frame-Options

X-Frame-Options tells the browser whether you want to allow your site to be framed or not. By preventing a browser from framing your site you can defend against attacks like clickjacking.

NginX: add_header X-Frame-Options "SAMEORIGIN" always;

Apache: Header always set X-Frame-Options "SAMEORIGIN"

For IIS, you add the header with Name = X-Frame-Options and Value = SAMEORIGIN.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="X-Frame-Options" content="SAMEORIGIN">

X-XSS-Protection

X-XSS-Protection sets the configuration for the cross-site scripting filter built into most browsers.

NginX: add_header X-Xss-Protection "1; mode=block" always;

Apache: Header always set X-Xss-Protection "1; mode=block"

For IIS, you add the header with Name = X-Xss-Protection and Value = 1; mode=block.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="X-Xss-Protection" content="1; mode=block">

X-Content-Type-Options

X-Content-Type-Options stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type.

NginX: add_header X-Content-Type-Options "nosniff" always;

Apache: Header always set X-Content-Type-Options "nosniff"

For IIS, you add the header with Name = X-Content-Type-Options and Value = nosniff.

If you can't set the headers for the system you're using you can try to use a meta tag in your HTML documents' header. Like this:

<meta http-equiv="X-Content-Type-Options" content="nosniff">

References

I learned a lot of this from Scott Helme, and he maintains a bunch of tools that I've used here. Definitely check out his website to learn more! Link