Set security headers on all responses

The app set no security headers at all, and the reverse proxy sets none
either.

- Content-Security-Policy: default-src 'none' with 'self' only for
  styles, images and fonts (MAX has no JavaScript), form-action 'self'
  (also blocks the http:// form action downgrade), frame-ancestors
  'none' and base-uri 'none'.
- X-Frame-Options: DENY, X-Content-Type-Options: nosniff,
  Referrer-Policy: same-origin.
- Strict-Transport-Security with max-age of 7 days, not in debug mode.

The headers are set in after_request, so they also apply to static
files, 404 and error responses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
finnandClaude Opus 5.5 committed 2026-10-21 18:17:19 +00:00
1 parent 931243dd60
commit 2fb357ae77
2 files changed
+31

No files matched your search

+2
View File
@@ -16,6 +16,7 @@ from .middlewares import (
add_middleware_stack,
)
from .routes import init_routes
from .security_headers import init_security_headers
from .sessions import SessionInterface
from .translations import TranslationHandler
@@ -64,6 +65,7 @@ def main():
register_cli(app)
init_auth(app)
init_routes(app)
init_security_headers(app)
return app
+29
View File
@@ -0,0 +1,29 @@
CONTENT_SECURITY_POLICY = "; ".join(
(
"default-src 'none'",
"style-src 'self'",
"img-src 'self'",
"font-src 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
"base-uri 'none'",
)
)
HSTS_MAX_AGE = 7 * 24 * 60 * 60 # in seconds
def init_security_headers(app):
headers = {
"Content-Security-Policy": CONTENT_SECURITY_POLICY,
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "same-origin",
}
if not app.debug:
headers["Strict-Transport-Security"] = f"max-age={HSTS_MAX_AGE}"
# after_request also covers static files, 404 and error responses
@app.after_request
def set_security_headers(response):
response.headers.update(headers)
return response