Everyone wants to be secure on the web, but security is hard. There are many ways one can exploit a website but what if we told you just by setting a few HTTP Headers, you can reduce your attack surface area?
Scott Helme created securityheaders.com to help drive up the usage of security-based headers across the web. The tool scans a website, then displays a report with a rating from F to A+, informing you of extra security headers you could add to increase your site's security, what that header does and how it can protect you.
Let’s take a deep dive into the security headers we have set on www.blackpepper.co.uk.
Strict Transport Security
Strict Transport Security asks for the site to be loaded over HTTPS instead of HTTP so when the site is loaded again, your browser remembers and automatically connects over HTTPS. The only annoyance is the initial request can come over HTTP before the browser sees the header.
The header accepts the following values:
max-age=<expire-time> -- time in seconds
includeSubDomains (optional) -- applies the rule to subdomains
preload (optional) -- stated intent for being added to the HSTS preload list
We set the value of this header to:
max-age=31536000; includeSubdomains; preload
31536000 seconds being 1 year.
X Content Type Options
so that's what we set it as.
X Frame Options
Frames/ iframes are a tool that can improve user experience, e.g. PayPal opening a frame to allow for payment. They can also be used for malicious intent e.g. clickjacking attacks. Frame Options specifies rules around whether the browser should be allowed to render pages in frames, iframes, embeds or objects.
It accepts the following values:
deny -- disallows all iframes
sameorigin -- only allows iframes from the same origin
We set this to:
This reduces the attack surface area as now the attacker would have to inject their own persistent malicious iframe code into our website to attempt to pull off a clickjacking attack.
When navigating from one site to another, the Referrer Policy header, (originally called the referer header, yes, spelt incorrectly…), keeps track of where you have just been. Referrer Policy allows for control over this header via the scheme and origin e.g. HTTP or HTTPS.
It accepts the following values:
"" -- rules for referer header not set
no-referrer -- disallows site url to appear in the referer header at all
no-referrer-when-downgrade -- only allows site url in the referer header if navigated site is not over HTTP
same-origin -- only allows site url in the referer header if same origin
origin -- allows site url to appear in referer header, but strips away path information
strict-origin -- same as origin but only if navigated site is not over HTTP
origin-when-cross-origin -- same as origin but only when navigating to a non origin site
strict-origin-when-cross-origin -- same as origin-when-cross-origin but only if navigated site is not over HTTP
unsafe-url -- the full url is always set in the referer header
We went with the following to keep off of HTTP websites:
The Permissions Policy header, which is the new improved feature policy header, allows for control over specific permissions in your browser on your site e.g. autoplay, fullscreen, geolocation and microphone.
This header accepts the following format: permissions-policy: <permission>=(<control level>).
For <permission>, there is a vast array of values that can be configured, check out here to see the full list.
The <control level> parameter accepts the following values:
() -- disallows the feature
self -- feature allowed on the current page and in iframes on the origin
src (iframe only) -- feature allowed on in the current iframe, as long as it comes frame the same origin as website
[origin] -- a specified list of origins the feature is allowed on, space-separated
We set this to:
Permissions-Policy: Permissions-Policy: geolocation=(self), midi=(), payment=(), fullscreen=(self), gyroscope=(), magnetometer=(), camera=(), microphone=(), sync-xhr=(self), usb=(), picture-in-picture=(), autoplay=()
Content Security Policy
The header accepts the following format: <source> <control level>.
The parameter accepts a large list of values, some of which include:
default-src -- the fallback for non-specified sources
script-src -- policy for executable scripts (JS)
style-src -- policy for styling (CSS)
image-src -- policy for images
frame-src -- policy for iframes/ frames
The parameter allows the following values:
none -- blocks use of this source
self -- allows the use of the source for current origin only
unsafe-inline -- allows the use of inline CSS and JS
unsafe-eval -- allows use of eval() JS function
<url> -- allows source from an exact URL (supports * wildcard) e.g. nikmouz.dev:*
The full list can be found here, along with more detailed information.
Warning! You do need to be careful when setting this header. As it will block assets being loaded, a mis-configured header value could very well break your website. Before setting the Content Security Policy, we recommend testing it out via setting the Content Security Policy Report Only header, this will log violations of your policy in the console of your browser instead of blocking assets. We decided to set the header as:
Content-Security-Policy-Report-Only: default-src https:; style-src 'unsafe-inline' https:; script-src 'unsafe-inline' https:
This policy will block any content loaded over HTTP, in the future I would like to also block inline JS and CSS, however, this is not possible at the current time.
We are currently trialling this header in report-only mode.
As you can see security headers are extremely powerful, easy to implement and have a huge payoff by removing the potential of certain exploits. Let's do a scan on our site and see the results.
Try it out on your own website and see how you score!