diff --git a/backend/settings/base.py b/backend/settings/base.py index 35cb80ddda..1f1c4c5b3b 100644 --- a/backend/settings/base.py +++ b/backend/settings/base.py @@ -93,6 +93,7 @@ class Base(Configuration): MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "apps.common.middlewares.csrf_referer_fallback.CsrfRefererFallbackMiddleware", @@ -241,6 +242,14 @@ class Base(Configuration): SLACK_EVENTS_ENABLED = True SLACK_SIGNING_SECRET = values.SecretValue() + SELF_CONSTANT = "'self'" SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_SSL_REDIRECT = False + X_FRAME_OPTIONS = "DENY" + SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin" + + # HSTS Settings (Strict-Transport-Security) + SECURE_HSTS_SECONDS = 31536000 # 1 year + SECURE_HSTS_INCLUDE_SUBDOMAINS = True + SECURE_HSTS_PRELOAD = False diff --git a/backend/settings/local.py b/backend/settings/local.py index 914fcbecb3..d83e2145a1 100644 --- a/backend/settings/local.py +++ b/backend/settings/local.py @@ -32,3 +32,7 @@ class Local(Base): SLACK_COMMANDS_ENABLED = True SLACK_EVENTS_ENABLED = True + + # HSTS Overrides for local development + SECURE_HSTS_SECONDS = 0 + SECURE_HSTS_PRELOAD = False diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 94eb3a2bd0..1bbc47ccd8 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,3 +1,4 @@ + import { withSentryConfig } from '@sentry/nextjs' import type { NextConfig } from 'next' @@ -6,6 +7,36 @@ const isLocal = process.env.NEXT_PUBLIC_ENVIRONMENT === 'local' const nextConfig: NextConfig = { devIndicators: false, + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + ...(isLocal ? [] : [ + { + key: 'Strict-Transport-Security', + value: 'max-age=31536000; includeSubDomains; preload' + }, + { + key: 'Content-Security-Policy', + value: [ + "default-src 'self'", + "script-src 'self' 'unsafe-inline'", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + // Removed the jogruber.de API host below + "connect-src 'self' https://*.ingest.sentry.io https://us.i.posthog.com" + ].join('; '), + } + ]), + { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, + ], + }, + ] + }, images: { // This is a list of remote patterns that Next.js will use to determine // if an image is allowed to be loaded from a remote source.