From 526d6142da509965f50b2e520662cc82769ab3de Mon Sep 17 00:00:00 2001 From: Yaron de Leeuw Date: Mon, 17 Oct 2016 16:00:16 -0400 Subject: [PATCH] Add security related headers to build.servo.org Add some headers to `build.servo.org` to improve our Mozilla Observatory score. The helps the cause of #473, by raising our score from F (0) to a whopping B- (65). The testing is still basic, the docker-observatory based testing will go in a different PR. To get to an A+ we must first implement https. This is the local observatory report: Score Rule Description -20 redirection Does not redirect to an https site. -20 strict-transport-security HTTP Strict Transport Security (HSTS) header cannot be set for sites not available over https. -5 contribute Contribute.json file missing from root of website. 5 content-security-policy Content Security Policy (CSP) implemented without 'unsafe-inline' or 'unsafe-eval'. 5 x-frame-options X-Frame-Options (XFO) implemented via the CSP frame-ancestors directive. Score: 65 Grade: B- --- nginx/https_headers.conf | 15 +++++++++++++++ nginx/init.sls | 9 +++++++++ tests/sls/nginx/headers.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 nginx/https_headers.conf create mode 100644 tests/sls/nginx/headers.py diff --git a/nginx/https_headers.conf b/nginx/https_headers.conf new file mode 100644 index 00000000..fc9e7339 --- /dev/null +++ b/nginx/https_headers.conf @@ -0,0 +1,15 @@ +# The following headers were added to pass the mozilla http observatory +# test suit. +# https://wiki.mozilla.org/Security/Guidelines/Web_Security + +# All of them are very simple in this case, because both homu and buildbot +# do not use external resources. + +# CSP is the most important one +add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'"; +# Block site from being framed +add_header X-Frame-Options DENY; +# Block pages from loading when they detect reflected XSS attacks +add_header X-XSS-Protection "1; mode=block"; +# Prevent browsers from incorrectly detecting non-scripts as scripts +add_header X-Content-Type-Options nosniff; diff --git a/nginx/init.sls b/nginx/init.sls index 94ede988..241bb8c3 100644 --- a/nginx/init.sls +++ b/nginx/init.sls @@ -14,6 +14,15 @@ nginx: - watch_in: - service: nginx +/etc/nginx/conf.d/https_headers.conf: + file.managed: + - source: salt://nginx/https_headers.conf + - user: root + - group: root + - mode: 644 + - watch_in: + - service: nginx + /etc/nginx/sites-enabled/default: file.symlink: - target: /etc/nginx/sites-available/default diff --git a/tests/sls/nginx/headers.py b/tests/sls/nginx/headers.py new file mode 100644 index 00000000..8b5c8c95 --- /dev/null +++ b/tests/sls/nginx/headers.py @@ -0,0 +1,30 @@ +""" +Test nginx's security related headers + +Test the headers very naively - their existence and value. Only check the +top-most '/' domain. +""" +import urllib.request + +from tests.util import Failure, Success + + +def run(): + expected_headers = [ + ('Content-Security-Policy', + "default-src 'self'; frame-ancestors 'none'"), + ('X-Frame-Options', 'DENY'), + ('X-XSS-Protection', '1; mode=block'), + ('X-Content-Type-Options', 'nosniff')] + with urllib.request.urlopen('http://localhost/') as local_open: + actual_headers = local_open.getheaders() + failures = [] + for header in expected_headers: + if header not in actual_headers: + failures.append('Missing or changed header - {}:{}'.format(*header)) + + if len(failures) > 0: + return Failure('nginx is serving wrong securirty headers', + '\n'.join(failures)) + else: + return Success('nginx is serving the correct security headers')