diff --git a/README.md b/README.md index b898118f1..c0bbe6160 100644 --- a/README.md +++ b/README.md @@ -511,11 +511,11 @@ We use OWASP ZAP for security scans. Here is an [intro to OWASP ZAP](https://res You can run and pull down the container to use locally: - docker pull owasp/zap2docker-weekly + docker pull softwaresecurityproject/zap-stable Run OWASP ZAP security scans with docker using the GUI: - docker run -u zap -p 8080:8080 -p 8090:8090 -i owasp/zap2docker-weekly zap-webswing.sh + docker run -u zap -p 8080:8080 -p 8090:8090 -i softwaresecurityproject/zap-stable zap-webswing.sh you can see the GUI at http://localhost:8080/zap/ @@ -531,8 +531,17 @@ Then you can stop the container with: Run OWASP ZAP security scans with docker using the command line. Here is an example of running the baseline, passive scan locally targeting the development site: - docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-weekly zap-baseline.py \ - -t https://crt-portal-django-dev.app.cloud.gov/report/ -c .circleci/zap.conf -z "-config rules.cookie.ignorelist=django_language" +``` +docker run -v $(pwd):/zap/wrk/:rw -t softwaresecurityproject/zap-stable zap-baseline.py -t https://crt-portal-django-dev.app.cloud.gov/report/ -c .circleci/zap.conf --autooff -z "-config rules.cookie.ignorelist=django_language" +``` + +And here's targeting a local devserver (note that "--net=host" allows it to connect to reach the devserver at localhost): + +Note that the devserver serves Django 404 pages when DEBUG=True is enabled, and these pages will fail some security (such as CSP). + +``` +docker run --net=host -v $(pwd):/zap/wrk/:rw -t softwaresecurityproject/zap-stable zap-baseline.py -t http://localhost:8000/report/ -c .circleci/zap.conf --autooff -z "-config rules.cookie.ignorelist=django_language" +``` That will produce a report locally that you can view in your browser. It will give you a list of things that you should check. Sometimes there are things at the low or informational level that are false positives or are not worth the trade-offs to implement. The report will take a minute or two to generate. diff --git a/crt_portal/crt_portal/settings.py b/crt_portal/crt_portal/settings.py index 05c8730af..6d409dda6 100644 --- a/crt_portal/crt_portal/settings.py +++ b/crt_portal/crt_portal/settings.py @@ -18,6 +18,7 @@ import django.conf.locale from django.utils.log import DEFAULT_LOGGING from django.utils.translation import gettext_lazy as _ +from csp.constants import SELF, NONCE # Are we in a test environment? @@ -100,6 +101,7 @@ 'compressor_toolkit', 'storages', 'formtools', + 'csp', # 'django_auth_adfs' in production only 'crequest', 'rest_framework', @@ -123,6 +125,7 @@ 'django.middleware.locale.LocaleMiddleware', 'crt_portal.locale_middleware.LanguageParamMiddleware', 'crequest.middleware.CrequestMiddleware', + 'csp.middleware.CSPMiddleware', ] @@ -390,9 +393,8 @@ else: env_csp_sources = [] -MIDDLEWARE.append('csp.middleware.CSPMiddleware') allowed_sources = ( - "'self'", + SELF, 'www.civilrights.justice.gov', 'civilrights.justice.gov', 'https://touchpoints.app.cloud.gov', @@ -414,76 +416,83 @@ X_FRAME_OPTIONS = "DENY" # If this is set to True, client-side JavaScript will not be able to access the language cookie. SESSION_COOKIE_HTTPONLY = True -# see settings options https://django-csp.readthedocs.io/en/latest/configuration.html#configuration-chapter -CSP_DEFAULT_SRC = allowed_sources +# See https://django-csp.readthedocs.io/en/latest/configuration.html +# Note we are on 4.0+ + +CONTENT_SECURITY_POLICY = { + 'EXCLUDE_URL_PREFIXES': ('/admin'), # Allow admin panel functionality (which is trusted content that uses inline sources) + 'DIRECTIVES': { + 'default-src': allowed_sources, + 'script-src': ( + SELF, + NONCE, + 'www.civilrights.justice.gov', + 'civilrights.justice.gov', + 'https://dap.digitalgov.gov', + 'https://www.google-analytics.com', + 'https://stats.g.doubleclick.net', + 'https://touchpoints.app.cloud.gov', + 'https://www.googletagmanager.com/', + 'https://cdnjs.cloudflare.com/', + 'https://challenges.cloudflare.com/', + 'https://www.google.com/', + 'a.tile.openstreetmap.org', # For loading image tiles in map data + 'b.tile.openstreetmap.org', # For loading image tiles in map data + 'c.tile.openstreetmap.org', # For loading image tiles in map data + *env_csp_sources, + ), + 'connect-src': ( + SELF, + 'www.civilrights.justice.gov', + 'civilrights.justice.gov', + 'https://dap.digitalgov.gov', + 'https://www.google-analytics.com', + 'https://stats.g.doubleclick.net', + 'https://touchpoints.app.cloud.gov', + 'https://www.googletagmanager.com/', + 'https://cdnjs.cloudflare.com/', + 'https://challenges.cloudflare.com/', + 'https://www.google.com/', + 'a.tile.openstreetmap.org', # For loading image tiles in map data + 'b.tile.openstreetmap.org', # For loading image tiles in map data + 'c.tile.openstreetmap.org', # For loading image tiles in map data + *env_csp_sources, + ), + 'img-src': ( + *allowed_sources, + 'data:', + 'a.tile.openstreetmap.org', # For loading image tiles in map data + 'b.tile.openstreetmap.org', # For loading image tiles in map data + 'c.tile.openstreetmap.org', # For loading image tiles in map data + ), + 'media-src': allowed_sources, + 'frame-src': allowed_sources, + 'worker-src': ( + *allowed_sources, + 'blob:' + ), + 'frame-ancestors': allowed_sources, + 'style-src': ( + SELF, + 'www.civilrights.justice.gov', + 'civilrights.justice.gov', + "'unsafe-inline'", + 'https://fonts.googleapis.com', + *env_csp_sources, + ), + 'font-src': ( + SELF, + 'www.civilrights.justice.gov', + 'civilrights.justice.gov', + "'unsafe-inline'", + 'https://fonts.gstatic.com', + *env_csp_sources, + ), + } +} + SESSION_COOKIE_SAMESITE = 'Lax' -CSP_SCRIPT_SRC = ( - "'self'", - 'www.civilrights.justice.gov', - 'civilrights.justice.gov', - 'https://dap.digitalgov.gov', - 'https://www.google-analytics.com', - 'https://stats.g.doubleclick.net', - 'https://touchpoints.app.cloud.gov', - 'https://www.googletagmanager.com/', - 'https://cdnjs.cloudflare.com/', - 'https://challenges.cloudflare.com/', - 'https://www.google.com/', - 'a.tile.openstreetmap.org', # For loading image tiles in map data - 'b.tile.openstreetmap.org', # For loading image tiles in map data - 'c.tile.openstreetmap.org', # For loading image tiles in map data - *env_csp_sources, -) -CSP_CONNECT_SRC = ( - "'self'", - 'www.civilrights.justice.gov', - 'civilrights.justice.gov', - 'https://dap.digitalgov.gov', - 'https://www.google-analytics.com', - 'https://stats.g.doubleclick.net', - 'https://touchpoints.app.cloud.gov', - 'https://www.googletagmanager.com/', - 'https://cdnjs.cloudflare.com/', - 'https://challenges.cloudflare.com/', - 'https://www.google.com/', - 'a.tile.openstreetmap.org', # For loading image tiles in map data - 'b.tile.openstreetmap.org', # For loading image tiles in map data - 'c.tile.openstreetmap.org', # For loading image tiles in map data - *env_csp_sources, -) -CSP_IMG_SRC = ( - *allowed_sources, - 'data:', - 'a.tile.openstreetmap.org', # For loading image tiles in map data - 'b.tile.openstreetmap.org', # For loading image tiles in map data - 'c.tile.openstreetmap.org', # For loading image tiles in map data -) -CSP_MEDIA_SRC = allowed_sources -CSP_FRAME_SRC = allowed_sources -CSP_WORKER_SRC = ( - *allowed_sources, - 'blob:' -) -CSP_FRAME_ANCESTORS = allowed_sources -CSP_STYLE_SRC = ( - "'self'", - 'www.civilrights.justice.gov', - 'civilrights.justice.gov', - "'unsafe-inline'", - 'https://fonts.googleapis.com', - *env_csp_sources, -) -CSP_FONT_SRC = ( - "'self'", - 'www.civilrights.justice.gov', - 'civilrights.justice.gov', - "'unsafe-inline'", - 'https://fonts.gstatic.com', - *env_csp_sources, -) -CSP_INCLUDE_NONCE_IN = ['script-src'] -# Allow admin panel functionality (which is trusted content that uses inline sources): -CSP_EXCLUDE_URL_PREFIXES = ('/admin') + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/