As our readers know, we're vocal on WordPress security and site security in general. We've created a how-to-guide on advanced security through hardening browser headers including HSTS, HPKP and CSP.
Security improvements are always a work in progress. Despite being thorough with our security implementation, we realise there will always be vulnerabilities. With a determined attacker – if there is a will and enough resources in play – then even ardent security conscience site owners will be up against it.
That said, we’re surprised when some recognised online brands still don’t adopt the most stringent Transport Layer Security (TLS) options.
How stringent do these methods have to be? Hypertext Transport Protocol Secure (HTTPS) is a great start, but we discuss the methods beyond HTTPS in this post. If you have the inclination to implement them, then you’ll be in the top 0.005% of sites in the world when it comes to security. Sounds awesome, right?
I just want to also state here that while this post is about advanced security for WordPress, it is easily applicable to sites that use other CMS systems.
Table Of Contents
- 1 HTTP Strict Transport Security and beyond
- 2 HSTS (HTTP Strict Transport Security)
- 3 Which sites in the world are using HSTS
- 4 HTTP Public Key Pinning (HPKP)
- 5 Content Security Policy (CSP)
HTTP Strict Transport Security and beyond
HTTP Strict Transport Security, or HSTS, is a part of browser security that tells browsers that it should be communicated to using HTTPS and not via HTTP.
It tells the browser to never load the site using HTTP and then to redirect it to HTTPS. This redirection gap is a prime gap for a ‘man-in-the-middle’ (MITM) attack as it’s commonly known. Instead, HSTS tells the browser that it should convert all attempts using HTTP to HTTPS.
For example, if a returning visitor entered https://mentorithm.com, the HSTS will tell the browser to convert it to https://mentorithm.com instead, therefore bypassing any potential MITM vulnerabilities.
This 'D' score from SecurityHeaders shows there are a few things that we still need to implement to attain the highest level of security. That is to say, we need to implement Content Security Policy, have Public Key Pins, X-Frame Options and X-XSS Protection.
These headers that we'll discuss below are connected to Secure Sockets Layer (SSL) implementation i.e. if the site has SSL you can implement security headers. Only Content Security Policy (CSP) does not require that you have SSL as a prerequisite.
So we begin with an SSL check to see the condition of it.
SSL TLS Checkup
We can use the Qualys SSL Labs to check this. Our results are as follows:
The A+ grade means that we don’t have any mixed issues and we are forcing SSL everywhere.
We can also check this directly using the ‘inspect’ button in the browser and then going to the 'security' tab. If there were any mixed content issues or conflicting security issues, these would be flagged up by red dots with a message telling you what the issues are.
There is an add-on from chrome called SSL checker that we’ve inked to below and you can also use Google Developer tools to do some checks.
We suggest you carry this out in incognito mode as the results can be affected by any add-ons or extensions. You may choose to disable them while carrying out the checks.
Are we all done with security? Not quite. SSL is not enough and it’s recommended to add other headers to improve security.
The first header we’ll discuss is the widest browser support called HSTS. The issue is that it’s only supported by 82% of clients. It’s backwards compatible, meaning that if browsers don’t support this header, the site will still load correctly. It’s just that the header itself won’t be executed.
In time, more browsers will adopt this as it’s important.
HSTS (HTTP Strict Transport Security)
We discussed what HSTS is earlier. As shown by the image above, CanIUse confirms that 82.7% of browsers currently support HSTS.
We can check what steps the browser actually takes by inspecting the site. If we go to network, we get the following:
The screenshot shows a 307 as we refreshed the page. It started originally with a 200. This means that our certificate ensured no 301 redirect from HTTP to HTTPS i.e. it was converted before the information was loaded. By extension of this, we eliminate any MITM attacks that could occur in the gap between a 301 redirect to HTTPS.
What a Strict Transport Security header includes
A strict transport security header would have the following for example:
max-age=155552000; includeSubDomains; preload
This basically means that the browser will only preload a HTTPS header without the need of redirecting from HTTP to HTTPS.
It declares that a client should interact with a site over HTTPS only.
HSTS relies on trust on first use (TOFU ) So if the first time it is loaded with HSTS, the browser will remember this for the time period allocated in seconds by max-age.
The next time the site loads, should there be any changes or compromise with the header, the browser will tell you and the lock should turn red. Remember that each new visit entry will reset this STS time.
If the site for example has images that is supported with HTTPS, but an attacker sends an image that doesn’t use it, the browser interprets this as the attacker trying to downgrade the site security. HSTS will prevent this.
Checking for HSTS
Here you can check if you have HSTS in Google Chrome: chrome://net-internals/#hsts under a query domain. For example, with mentorithm.com, we get:
The above result shows that our site is implementing strict transfer security. Some of them are still showing as blank above as we’ve relatively recently submitted our site. It takes a few months for the browser to recognise it. What is good though is that our domain and subdomain are already strict.
You can also do the same check in Firefox:
1. Open File Explorer
2. Copy paste %APPDATA%\Mozilla\Firefox\Profiles\ in the address bar of file explorer (for Linux it is ~/.mozilla/firefox)
3. Double click the folder you see (if you have multiple FF profiles, there will be multiple folders)
4. Open SiteSecurityServiceState.txt. This text file contains sites that have enabled HSTS.
The Chrome add-on mentioned earlier does this for Google Chrome.
Preloading your site – tell browsers your site is HTTPS
As mentioned above, browsers use a TOFU principle. However, until it first visit has been made to your site, then their browser won't know about HSTS. To resolve this, you can 'pre-load' and get it built into the browser. Once submitted and accepted as a HTTPS site only, the browser will know that the accepted site will only ever be HTTPS.
This is the site to go and submit to: https://hstspreload.appspot.com/
However, prior to acceptance, you would need to add the preload directive in an HSTS header and then meet the requirements:
- Serve a valid certificate.
- Redirect from HTTP to HTTPS on the same host.
- Serve all subdomains over HTTPSi.
- Serve an HSTS header on the base domain for HTTPS requestsii.
i In particular, you must support HTTPS for the www subdomain if a DNS record for that subdomain exists.
ii The max-age must be at least eighteen weeks (10886400 seconds).The includeSubDomains directive must be specified.The preload directive must be specified.If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS header (rather than the page it redirects to).
CloudFlare and HSTS
You can submit to HSTS once you've signed to CloudFlare. It works just fine, but as far as we can tell, you are not submitted to the hststpreload submission site mentioned above. Neverthless, if you check the header on any browser, it seems to be working fine. The only difference is that you can set adjust the max-time, but to pass the official submission the max-age must be at least eighteen weeks
Which sites in the world are using HSTS
With hundreds of millions of domains in the world, which ones are committed to and enabling the preloading of HSTS? Surprisingly, very few. Using this link at Google Git last year, it was around 3,000(!). A slight improvement has seen that rise to 15,330 at time of writing. Our submission will bring this up to 15,331 hopefully! This means that only 0.005% of the sites in the world have submitted and committed to HSTS.
Once you've submitted your HSTS site to preloading, it takes few months until the site is preloaded into the browser.
HTTP Public Key Pinning (HPKP)
Even though the site is protected by SSL and HSTS, your site can still be vulnerable.
Attackers can exploit the site by issuing a new fraudulent certificate instead of the one that is already installed. By impersonating a site, the attacker could then gain access to sensitive data and key credentials. HTTP Public Key Pinning (HKPK) helps to ensure that attacker won’t be able to do this by specifying the exact certificate with the server.
In other words, HKPK will tell the browser which public key from a certificate authority (CA) belongs to the server, thus stopping a MITM attack in its tracks.
As it stands 60% of browsers use HKPK as displayed by the screenshot below. This has gone up by about 20% from when we last had a look in 2015.
Due to the complicated nature of HKPK, it isn’t recommended that every site owners installs it. There is a risk that if you don’t know exactly what you’re doing or you don’t make backups, you could break your site through a self-inflicted denial of service.
How does HKPK work in practice?
Current HPKP implementation is done through Trust on First Use (TOFU), meaning the browser will trust the certificate the first time it’s accessed and is whitelisted. The browser then stores this information for the period of time discussed above i.e. max-age. The next time it lands on your site, it will expect the same public key pins from the certificate.
To implement HPKP, the site will need to return the public key pins HTTP header when the site is accessed over HTTPS.
Public-Key-Pins: pin-sha256=”base64==”; max-age=expireTime [; includeSubDomains][; report-uri=”reportURI”]
pin-sha256: The public keys on the certificate.
max-age: how long a browser should pin the defined keys (in seconds). A longer value is considered to be more secure.
includeSubDomains: An optional parameter to specify all subdomains to the same defined pins
report-uri: Another optional parameter that notifies the URL of any failed pin validations.
An example of a HPKP header can be found at https://developer.mozilla.org/en/docs/Web/Security/Public_Key_Pinning
Public-Key-Pins: pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; max-age=5184000; includeSubDomains; report-uri="https://www.example.net/hpkp-report"
The 5184000 tells the browser to store this information for two months. You’ll also notice two declarations i.e. if one expires, then we have a second as backup. It is always the case that there are at least two declarations.
The second pin-sha256 value is a public key that should not be in your current certificate chain.
You will need to create the pin keys for a public certificate by submitting a Certificate Signing Request (CSR) in order to get a valid certificate from a CA.
To create the CSRs in Apache, the links below explains how to do this for your respective servers, most of which we’ve covered below. In summary, it's as follows:
1. Log in to your server's terminal (SSH).
2. At the prompt, type the following command:
openssl req -new -newkey rsa:2048 -nodes -keyout yourdomain.key -out yourdomain.csr
3. Enter the requested information:
4. Open the CSR in a text editor and copy all of the text.
5. Paste the full CSR into the SSL enrollment form in your account.
Report Uri: https://report-uri.io/home/pkp_hash
It has to be said that not everyone is a fan of HPKP and there is a growing thought that implementing HPKP causes more problems than it solves. So whether it is this or CSP described below, you need to be sure of your methods.
Content Security Policy (CSP)
This additional layer of security called Content Security Policy (CSP) is similar to HSTS. It is a HTTP response header that helps to protect against injection based attacks such as reducing XSS risks on browsers. It functions by giving the browser an approved list or whitelist of loadable dynamic resources via a HTTP header.
Bear in mind that you don't need to have HTTPS to enable CSP i.e. this security enhancement can be done without HTTPS.
CSP is recommended on high-risk websites that need additional layers of security. However, much like HPKP, if you make a wrong rule, you can screw things up!
Compliancy isn’t too bad at around 85% as shown by this screenshot from CanIUse. This figure is up from previous years (from around 18 months ago when it was approximately 50% if we recall correctly).
Most browsers now support CSP, but if a user has an older browser, the CSP will not be implemented on that browser.
Each rule you set is regarding a specific element in the site. Remember to set rules that don’t contradict each other, otherwise it could lead to a self-inflicting denial of service. For this reason, it’s not recommended that CSP is implemented on older, existing websites if you're not completely confident about the structure of it.
If you’re not comfortable coding into .htaccess, it’s something that you should avoid.
'We hope that increased attention to this area will also encourage researchers to find new, creative ways to circumvent CSP restrictions, and help us further improve the mechanism so that we can better protect Internet users from web threats'
How to enable CSP
You can use a WordPress plugin (linked below) or you can add code to your .htaccess file.
Firstly, we need to declare where a site can load content from. By declaring keywords (or supportive directives) and hosts (or source) The CSP Quick Reference Guide is an excellent resource that will allow you to manually generate a content security policy header.
Defines valid sources of stylesheets.
Defines valid sources of images.
Applies to XMLHttpRequest (AJAX), WebSocket or EventSource. If not allowed the browser emulates a 400 HTTP status code.
Defines valid sources of fonts.
Defines valid sources of plugins, eg <object>, <embed> or <applet>.
Defines valid sources of audio and video, eg HTML5 <audio>, <video> elements.
Defines valid sources for loading frames. child-src is preferred over this deprecated directive.
Enables a sandbox for the requested resource similar to the iframe sandbox attribute. The sandbox applies a same origin policy, prevents popups, plugins and script execution is blocked. You can keep the sandbox value empty to keep all restrictions in place, or add values: allow-forms allow-same-origin allow-scripts allow-popups, and allow-top-navigation
Instructs the browser to POST a reports of policy failures to this URI. You can also append -Report-Only to the HTTP header name to instruct the browser to only send reports (does not block anything).
Defines valid sources for web workers and nested browsing contexts loaded using elements such as <frame> and <iframe>
Defines valid sources that can be used as a HTML <form> action.
Defines valid sources for embedding the resource using <frame> <iframe> <object> <embed> <applet>. Setting this directive to 'none' should be roughly equivalent to X-Frame-Options: DENY
Defines valid MIME types for plugins invoked via <object> and <embed>. To load an <applet> you must specify application/x-java-applet.
Wildcard, allows any URL except data: blob: filesystem: schemes.
Prevents loading resources from any source.
Allows loading resources from the same origin (same scheme, host and port).
Allows loading resources via the data scheme (eg Base64 encoded images).
Allows loading resources from the specified domain name.
Allows loading resources from any subdomain under example.com.
Allows loading resources only over HTTPS matching the given domain.
Allows loading resources only over HTTPS on any domain.
Allows use of inline source elements such as style attribute, onclick, or script tag bodies (depends on the context of the source it is applied to)
Content Security Policy Example
Some CSP examples could be:
This allows for everything to be loaded, but only from the same origin.
This allows loading scripts, but only from the same origin.
You can also combine CSPs:
If you also have analytics enabled from Google for example, you could allow this script and any from the current domain.
script-src ‘self’ www.google-analytics.com ajax.googleapis.com
Report Uri Tools: https://report-uri.io/home/tools
If there isn't enough security tips on this page to satisfy your thirst for web security, then we highly recommend that you visit Scott Helme's site who is an expert when it comes to web security.