|
203 | 203 | # External applications.
|
204 | 204 | "axes",
|
205 | 205 | "django_filters",
|
| 206 | + "csp", |
206 | 207 | "corsheaders",
|
207 | 208 | "vng_api_common",
|
208 | 209 | "notifications_api_common",
|
|
241 | 242 | "django.contrib.messages.middleware.MessageMiddleware",
|
242 | 243 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
|
243 | 244 | "axes.middleware.AxesMiddleware",
|
| 245 | + "csp.contrib.rate_limiting.RateLimitedCSPMiddleware", |
244 | 246 | ]
|
245 | 247 |
|
246 | 248 | ROOT_URLCONF = f"{PROJECT_DIRNAME}.urls"
|
|
559 | 561 | ),
|
560 | 562 | )
|
561 | 563 |
|
| 564 | +if IS_HTTPS: |
| 565 | + SECURE_HSTS_SECONDS = 31536000 |
| 566 | + |
562 | 567 | X_FRAME_OPTIONS = "DENY"
|
563 | 568 |
|
564 | 569 | #
|
@@ -932,3 +937,61 @@ def init_sentry(before_send: Callable | None = None):
|
932 | 937 | default=7,
|
933 | 938 | help_text="The amount of time after which request logs should be deleted from the database",
|
934 | 939 | ) # number of days
|
| 940 | + |
| 941 | + |
| 942 | +# |
| 943 | +# Django CSP settings |
| 944 | +# |
| 945 | +# explanation of directives: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy |
| 946 | +# and how to specify them: https://django-csp.readthedocs.io/en/latest/configuration.html |
| 947 | +# |
| 948 | +# NOTE: make sure values are a tuple or list, and to quote special values like 'self' |
| 949 | + |
| 950 | +# ideally we'd use BASE_URI but it'd have to be lazy or cause issues |
| 951 | +CSP_DEFAULT_SRC = [ |
| 952 | + "'self'", |
| 953 | +] + config("CSP_EXTRA_DEFAULT_SRC", default=[], split=True) |
| 954 | + |
| 955 | +CSP_REPORT_PERCENTAGE = config("CSP_REPORT_PERCENTAGE", 1.0) # float between 0 and 1 |
| 956 | + |
| 957 | +CSP_FORM_ACTION = ( |
| 958 | + config( |
| 959 | + "CSP_FORM_ACTION", |
| 960 | + default=["\"'self'\""] |
| 961 | + + config("CSP_EXTRA_FORM_ACTION", default=[], split=True), |
| 962 | + split=True, |
| 963 | + ) |
| 964 | + + CORS_ALLOWED_ORIGINS |
| 965 | +) |
| 966 | + |
| 967 | +CSP_IMG_SRC = CSP_DEFAULT_SRC + config("CSP_EXTRA_IMG_SRC", default=[], split=True) |
| 968 | + |
| 969 | +# affects <object> and <embed> tags, block everything by default but allow deploy-time |
| 970 | +# overrides. |
| 971 | +CSP_OBJECT_SRC = config("CSP_OBJECT_SRC", default=["\"'none'\""], split=True) |
| 972 | + |
| 973 | +# we must include this explicitly, otherwise the style-src only includes the nonce because |
| 974 | +# of CSP_INCLUDE_NONCE_IN |
| 975 | +CSP_STYLE_SRC = CSP_DEFAULT_SRC |
| 976 | +CSP_SCRIPT_SRC = CSP_DEFAULT_SRC |
| 977 | + |
| 978 | +# firefox does not get the nonce from default-src, see |
| 979 | +# https://stackoverflow.com/a/63376012 |
| 980 | +CSP_INCLUDE_NONCE_IN = ["style-src", "script-src"] |
| 981 | + |
| 982 | +# directives that don't fallback to default-src |
| 983 | +CSP_BASE_URI = ["'self'"] |
| 984 | + |
| 985 | +# Frame directives do not fall back to default-src |
| 986 | +CSP_FRAME_ANCESTORS = ["'none'"] # equivalent to X-Frame-Options: deny |
| 987 | +CSP_FRAME_SRC = ["'self'"] |
| 988 | +# CSP_NAVIGATE_TO = ["'self'"] # this will break all outgoing links etc # too much & tricky, see note on MDN |
| 989 | +# CSP_SANDBOX # too much |
| 990 | + |
| 991 | +CSP_UPGRADE_INSECURE_REQUESTS = False # TODO enable on production? |
| 992 | + |
| 993 | +CSP_EXCLUDE_URL_PREFIXES = ( |
| 994 | + # ReDoc/Swagger pull in external sources, so don't enforce CSP on API endpoints/documentation. |
| 995 | + "/api/", |
| 996 | + "/admin/", |
| 997 | +) |
0 commit comments