Site improvements - Headers and Certs edition
Analyzing jamesaimonetti.com
After reading Julia Evans' post on the HSTS header, I felt like I should add it to this site. This lead me to Mozilla's Observatory that will analyze your site and grade it. Like Julia, I received a fun F - the biggest dings were missing headers and an invalid certificate chain. Let's look at how I improved my site's scoring.
The initial site
My blog is a Nikola-powered site. Since the pages are fronted by Apache (for the time being) most of the changes need to be applied there (as Apache controls the response headers). Let's add some headers!
Setting the HTTP Strict Transport Security (HSTS)
This post talks more about preload and other considerations.
On the https (port 443) virtual host, its a simple one-line addition:
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
The max_age
is in seconds (63072000 is 2 years).
Setting the Content Security Policy (CSP) header
Mozilla has great help text about the header here.
Header always set Content-Security-Policy "default-src 'none'; font-src 'self'; img-src 'self'; object-src 'none'; script-src 'self'; style-src 'self'"
I've opted to keep everything in-house-only. If I start hosting images or fonts on other sites I can add them in as exceptions.
Certificate issues when redirecting
The redirection error seemed inappropriate as I use a permanent redirect from http to https:
<VirtualHost *:80>
ServerName jamesaimonetti.com
Redirect permanent / https://jamesaimonetti.com
</VirtualHost>
So I went digging a bit more.
One of the ideas was to support HTTP public key pinning (HPKP). A cursory view of Let's Encrypt discussion suggested that this might not be worth the trouble for a relatively low-value site like mine.
As it turns out, the addition of the HSTS header will likely improve this metric! Once we reload the site and rescan it with Observatory, we'll find out.
Setting the X-Content-Type-Options header
Pretty straightforward explanation and easily added:
Header always set X-Content-Type-Options "nosniff"
Setting the X-Frame-Options header
I don't want my site in an iframe (see here) so in it goes:
Header always set X-Frame-Options "DENY"
This also adds a setting to the CSP header:
Header always set Content-Security-Policy "frame-ancestors 'none'"
Setting the X-XSS-Protection header
Seems mostly for legacy browsers that don't support CSP (see here).
Header always set X-XSS-Protection "1; mode=block"
Testing the setup
Initial Errors
Once the headers were set, I reloaded the site in Apache and reloaded it in my browser. I verified the response headers were included but now I had a bunch of console errors to handle:
Content Security Policy: The page’s settings blocked the loading of a resource at self (“script-src https://jamesaimonetti.com”). Source: onfocusin attribute on DIV element. jamesaimonetti.com Content Security Policy: The page’s settings blocked the loading of a resource at self (“script-src https://jamesaimonetti.com”). Source: $('a.image-reference:not(.islink) img:no.... jamesaimonetti.com:637 Content Security Policy: The page’s settings blocked the loading of a resource at self (“script-src https://jamesaimonetti.com”). Source: moment.locale("en"); fancydates.... jamesaimonetti.com:637 Content Security Policy: The page’s settings blocked the loading of a resource at self (“script-src https://jamesaimonetti.com”). Source: (function(i,s,o,g,r,a,m){i['GoogleAna.... jamesaimonetti.com:640
Google Analytics
Looking at the CSP FAQ about adding the Google Analytics, it looks like I need to whitelist Google in the img-src
portion of my CSP.
Header always set Content-Security-Policy "img-src 'self' www.google-analytics.com"
This in and of itself did not address the issue as the inline javascript used to do the analytics isn't executed. The recommended way is to load the analytics javascript in its own file (vs inline in the html page). Currently in my conf.py
I've added a BODY_END
snippet with the google analytics setup. I've moved that snippet to /files/assets/js/ga.js
and included it in my EXTRA_HEAD_DATA
config:
EXTRA_HEAD_DATA = """
<script src="/assets/js/ga.js" type="text/javascript"/>
"""
Now I need to add google-analytics to my CSP script-src
as well:
Header always set Content-Security-Policy "script-src 'self' www.google-analytics.com"
Improvement
With these changes above, scorecard improved to a solid B!
Still getting dinged with the HSTS header and certificate chain issues.
Turns out I had only included 2 of the three SSL-related directives in Apache. The third line below was added:
SSLCertificateKeyFile "/etc/letsencrypt/live/jamesaimonetti.com/privkey.pem"
SSLCertificateFile "/etc/letsencrypt/live/jamesaimonetti.com/cert.pem"
SSLCertificateChainFile "/etc/letsencrypt/live/jamesaimonetti.com/chain.pem"
chain.pem
includes the intermediate certificates needed to complete the chain. See SSL Labs' SSLTest to find out more about your site.
Forward secrecy
For funzies, adding forward secrecy to Apache to bump those scores up (yay gamification!).
Disable RC4
RC4 is broken; don't offer it!
I added a ssl.conf
in conf.d
with this:
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"