diff --git a/uber/config.py b/uber/config.py index b2c7fbe01..e3557a019 100644 --- a/uber/config.py +++ b/uber/config.py @@ -1123,6 +1123,7 @@ def _unrepr(d): _unrepr(_config['appconf']) c.APPCONF = _config['appconf'].dict() c.SENTRY = _config['sentry'].dict() +c.HSTS = _config['hsts'].dict() c.REDISCONF = _config['redis'].dict() c.REDIS_PREFIX = c.REDISCONF['prefix'] c.REDIS_STORE = redis.Redis(host=c.REDISCONF['host'], port=c.REDISCONF['port'], db=c.REDISCONF['db'], decode_responses=True) diff --git a/uber/configspec.ini b/uber/configspec.ini index 84d119b29..605a2fd16 100644 --- a/uber/configspec.ini +++ b/uber/configspec.ini @@ -1881,6 +1881,11 @@ port = integer(default=6379) db = integer(default=0) prefix = string(default="") +[hsts] +max_age = integer(default=31536000) +preload = boolean(default=False) +include_subdomains = boolean(default=False) + [appconf] # This is all CherryPy configuration. @@ -1888,6 +1893,7 @@ prefix = string(default="") tools.add_email_to_error_page.on = boolean(default=True) tools.sentry_end_transaction.on = boolean(default=False) tools.sentry_start_transaction.on = boolean(default=False) +tools.secureheaders.on = boolean(default=False) # custom logging output: # turn off normal traceback and header logging on errors, instead use our custom verbose logger that prints more info diff --git a/uber/server.py b/uber/server.py index 73dca99cb..86ace23b9 100644 --- a/uber/server.py +++ b/uber/server.py @@ -46,6 +46,20 @@ def sentry_end_transaction(): cherrypy.request.sentry_transaction.__exit__(None, None, None) cherrypy.tools.sentry_end_transaction = cherrypy.Tool('on_end_request', sentry_end_transaction) +@cherrypy.tools.register('before_finalize', priority=60) +def secureheaders(): + headers = cherrypy.response.headers + hsts_header = 'max-age=' + str(c.HSTS['max_age']) + if c.HSTS['include_subdomains']: + hsts_header += '; includeSubDomains' + if c.HSTS['preload']: + if c.HSTS['max_age'] < 31536000: + log.error('HSTS only supports preloading if max-age > 31536000') + elif not c.HSTS['include_subdomains']: + log.error('HSTS only supports preloading if subdomains are included') + else: + hsts_header += '; preload' + headers['Strict-Transport-Security'] = hsts_header def _add_email(): [body] = cherrypy.response.body