Skip to content

Commit bb115f7

Browse files
committed
🔧[#42] add HSTS & CSP settings
1 parent a965065 commit bb115f7

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

open_api_framework/conf/base.py

+66
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
# External applications.
125125
"axes",
126126
"django_filters",
127+
"csp",
127128
"corsheaders",
128129
"vng_api_common",
129130
"notifications_api_common",
@@ -162,6 +163,7 @@
162163
"django.contrib.messages.middleware.MessageMiddleware",
163164
"django.middleware.clickjacking.XFrameOptionsMiddleware",
164165
"axes.middleware.AxesMiddleware",
166+
"csp.contrib.rate_limiting.RateLimitedCSPMiddleware",
165167
]
166168

167169
ROOT_URLCONF = f"{PROJECT_DIRNAME}.urls"
@@ -421,6 +423,9 @@
421423
CSRF_COOKIE_SECURE = IS_HTTPS
422424
CSRF_COOKIE_SAMESITE = config("CSRF_COOKIE_SAMESITE", "Strict")
423425

426+
if IS_HTTPS:
427+
SECURE_HSTS_SECONDS = 31536000
428+
424429
X_FRAME_OPTIONS = "DENY"
425430

426431
#
@@ -672,3 +677,64 @@ def init_sentry(before_send: Callable | None = None):
672677
LOG_OUTGOING_REQUESTS_MAX_AGE = config(
673678
"LOG_OUTGOING_REQUESTS_MAX_AGE", default=7
674679
) # number of days
680+
681+
682+
#
683+
# Django CSP settings
684+
#
685+
# explanation of directives: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
686+
# and how to specify them: https://django-csp.readthedocs.io/en/latest/configuration.html
687+
#
688+
# NOTE: make sure values are a tuple or list, and to quote special values like 'self'
689+
690+
# ideally we'd use BASE_URI but it'd have to be lazy or cause issues
691+
CSP_DEFAULT_SRC = [
692+
"'self'",
693+
] + config("CSP_EXTRA_DEFAULT_SRC", default=[], split=True)
694+
695+
CSP_REPORT_PERCENTAGE = config("CSP_REPORT_PERCENTAGE", 1.0) # float between 0 and 1
696+
697+
CSP_FORM_ACTION = (
698+
config(
699+
"CSP_FORM_ACTION",
700+
default=["\"'self'\""]
701+
+ config("CSP_EXTRA_FORM_ACTION", default=[], split=True),
702+
split=True,
703+
)
704+
+ CORS_ALLOWED_ORIGINS
705+
)
706+
707+
CSP_IMG_SRC = (
708+
CSP_DEFAULT_SRC
709+
+ config("CSP_EXTRA_IMG_SRC", default=[], split=True)
710+
)
711+
712+
# affects <object> and <embed> tags, block everything by default but allow deploy-time
713+
# overrides.
714+
CSP_OBJECT_SRC = config("CSP_OBJECT_SRC", default=["\"'none'\""], split=True)
715+
716+
# we must include this explicitly, otherwise the style-src only includes the nonce because
717+
# of CSP_INCLUDE_NONCE_IN
718+
CSP_STYLE_SRC = CSP_DEFAULT_SRC
719+
CSP_SCRIPT_SRC = CSP_DEFAULT_SRC
720+
721+
# firefox does not get the nonce from default-src, see
722+
# https://stackoverflow.com/a/63376012
723+
CSP_INCLUDE_NONCE_IN = ["style-src", "script-src"]
724+
725+
# directives that don't fallback to default-src
726+
CSP_BASE_URI = ["'self'"]
727+
728+
# Frame directives do not fall back to default-src
729+
CSP_FRAME_ANCESTORS = ["'none'"] # equivalent to X-Frame-Options: deny
730+
CSP_FRAME_SRC = ["'self'"]
731+
# CSP_NAVIGATE_TO = ["'self'"] # this will break all outgoing links etc # too much & tricky, see note on MDN
732+
# CSP_SANDBOX # too much
733+
734+
CSP_UPGRADE_INSECURE_REQUESTS = False # TODO enable on production?
735+
736+
CSP_EXCLUDE_URL_PREFIXES = (
737+
# ReDoc/Swagger pull in external sources, so don't enforce CSP on API endpoints/documentation.
738+
"/api/",
739+
"/admin/",
740+
)

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies = [
3939
"djangorestframework-gis>=1.0",
4040
"django-filter>=24.2",
4141
"drf-spectacular>=0.27.2",
42+
"django-csp>=3.8",
4243
"djangorestframework-inclusions>=1.2.0",
4344
"commonground-api-common>=1.12.1",
4445
"mozilla-django-oidc-db>=0.19.0",

0 commit comments

Comments
 (0)