Security headers

A few simple provisions were sufficient to make this blog GDPR compliant. Even less was required to get a high rating regarding security. As a first step, I've obtained certificates from the Let's Encrypt initiative and configured Hiawatha (our web server) accordingly:

VirtualHost {
        ...
        TLScertFile = /etc/hiawatha/tls/pdes-net.org.pem
        RequireTLS = yes, 31536000; includeSubDomains; preload
        ...
}

This configuration got an A+ rating from Qualys SSL labs.

However, there's more to the security of a website than transport encryption. For example, the Content Security Policy “provides a standard method for website owners to declare approved origins of content that browsers should be allowed to load on that website”. There are actually a number of these security headers, and after some research I came up with the following settings:

VirtualHost {
        ...
        CustomHeader = Vary: Accept-Encoding
        CustomHeaderClient = X-Frame-Options: sameorigin
        CustomHeaderClient = X-XSS-Protection: 1; mode=block
        CustomHeaderClient = X-Content-Type-Options: nosniff
        CustomHeaderClient = X-Robots-Tag: none
        CustomHeaderClient = X-Permitted-Cross-Domain-Policies: none
        CustomHeaderClient = Referrer-Policy: same-origin
        CustomHeaderClient = Expect-CT: enforce; max-age=3600
        CustomHeaderClient = Content-Security-Policy: frame-src 'self'; worker-src 'self'; connect-src *; default-src 'self'; img-src 'self' data: chrome-extension-resource:; font-src 'self' data:; object-src 'self'; media-src 'self' data:; manifest-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; base-uri 'none'
        ...
}

The CSP proved to be tricky since Chromium did not display images in SVG format with stricter settings. A post of April King finally provided the missing piece of the puzzle. Furthermore, MathJax only works in my implementation if I allow scripts and styles to be included 'unsafe-inline'. As a result of this latter setting, my current configuration achieves only a 'B+' instead of the 'A+' depicted below.

Update: I've cleaned up and simplified the CSP, which now reads

CustomHeaderClient = Content-Security-Policy: frame-ancestors 'none'; frame-src 'self'; default-src 'self'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; base-uri 'none'; form-action 'none'

That still gets a 'B+' because of the 'unsafe-inline' options.

Mike Kuketz listed a number of online scanners evaluating the implementation of security policies on arbitrary web sites. One of the most informative one of these tools is Mozilla's observatory, developed by the same April whose post had helped me with the CSP. ☺

../images/observatory.png