Skip to content

Commit eea1e52

Browse files
committed
🔧[#42] add HSTS & CSP settings
1 parent 44eb847 commit eea1e52

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

open_api_framework/conf/base.py

+72
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"
@@ -419,6 +421,9 @@
419421

420422
CSRF_COOKIE_SECURE = IS_HTTPS
421423

424+
if IS_HTTPS:
425+
SECURE_HSTS_SECONDS = 31536000
426+
422427
X_FRAME_OPTIONS = "DENY"
423428

424429
#
@@ -670,3 +675,70 @@ def init_sentry(before_send: Callable | None = None):
670675
LOG_OUTGOING_REQUESTS_MAX_AGE = config(
671676
"LOG_OUTGOING_REQUESTS_MAX_AGE", default=7
672677
) # number of days
678+
679+
680+
#
681+
# Django CSP settings
682+
#
683+
# explanation of directives: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
684+
# and how to specify them: https://django-csp.readthedocs.io/en/latest/configuration.html
685+
#
686+
# NOTE: make sure values are a tuple or list, and to quote special values like 'self'
687+
688+
# ideally we'd use BASE_URI but it'd have to be lazy or cause issues
689+
CSP_DEFAULT_SRC = [
690+
"'self'",
691+
] + config("CSP_EXTRA_DEFAULT_SRC", default=[], split=True)
692+
693+
# Allow any 'https:' host, as we don't know in advance which target is used by eHerkenning.
694+
# Behavior is also different between browsers regarding redirects, see:
695+
# https://stackoverflow.com/a/69439102 / https://github.com/w3c/webappsec-csp/issues/8
696+
CSP_FORM_ACTION = (
697+
config(
698+
"CSP_FORM_ACTION",
699+
default=["\"'self'\"", "https:"]
700+
+ config("CSP_EXTRA_FORM_ACTION", default=[], split=True),
701+
split=True,
702+
)
703+
+ CORS_ALLOWED_ORIGINS
704+
)
705+
706+
# * service.pdok.nl serves the tiles for the Leaflet maps (PNGs) and must be whitelisted
707+
# * the data: URIs are used by Leaflet (invisible pixel for memory management/image unloading)
708+
# and the signature component which saves the image drawn on the canvas as data: URI
709+
CSP_IMG_SRC = (
710+
CSP_DEFAULT_SRC
711+
+ ["data:", "https://service.pdok.nl/"]
712+
+ config("CSP_EXTRA_IMG_SRC", default=[], split=True)
713+
)
714+
715+
# affects <object> and <embed> tags, block everything by default but allow deploy-time
716+
# overrides.
717+
CSP_OBJECT_SRC = config("CSP_OBJECT_SRC", default=["\"'none'\""], split=True)
718+
719+
# we must include this explicitly, otherwise the style-src only includes the nonce because
720+
# of CSP_INCLUDE_NONCE_IN
721+
CSP_STYLE_SRC = CSP_DEFAULT_SRC
722+
CSP_SCRIPT_SRC = CSP_DEFAULT_SRC
723+
724+
# firefox does not get the nonce from default-src, see
725+
# https://stackoverflow.com/a/63376012
726+
CSP_INCLUDE_NONCE_IN = ["style-src", "script-src"]
727+
728+
# directives that don't fallback to default-src
729+
CSP_BASE_URI = ["'self'"]
730+
731+
# Frame directives do not fall back to default-src
732+
CSP_FRAME_ANCESTORS = ["'none'"] # equivalent to X-Frame-Options: deny
733+
CSP_FRAME_SRC = ["'self'"]
734+
# CSP_NAVIGATE_TO = ["'self'"] # this will break all outgoing links etc # too much & tricky, see note on MDN
735+
# CSP_SANDBOX # too much
736+
737+
CSP_UPGRADE_INSECURE_REQUESTS = False # TODO enable on production?
738+
739+
CSP_EXCLUDE_URL_PREFIXES = (
740+
# ReDoc/Swagger pull in external sources, so don't enforce CSP on API endpoints/documentation.
741+
"/api/",
742+
# FIXME: Admin pulls in bootstrap from CDN & has inline styles/scripts probably
743+
"/admin/",
744+
)

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies = [
3838
"djangorestframework>=3.12.4",
3939
"djangorestframework-gis>=1.0",
4040
"django-filter>=23.2",
41+
"django-csp>=3.8",
4142
"drf-spectacular>=0.27.0",
4243
"djangorestframework-inclusions>=1.2.0",
4344
"commonground-api-common>=1.12.1",

0 commit comments

Comments
 (0)