diff --git a/server/conf/nginx/nginx.conf b/server/conf/nginx/nginx.conf index e28afdde4c..1b280da101 100644 --- a/server/conf/nginx/nginx.conf +++ b/server/conf/nginx/nginx.conf @@ -65,7 +65,6 @@ http { ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; - # gzip on; # gzip_types application/javascript image/* text/css; # gunzip on; diff --git a/server/portal/settings/settings.py b/server/portal/settings/settings.py index d576634e3f..28942b9f05 100644 --- a/server/portal/settings/settings.py +++ b/server/portal/settings/settings.py @@ -352,6 +352,9 @@ def portal_filter(record): 'formatter': 'metrics', 'filters': ['portalFilter'] }, + "csp_file": { + "class": "logging.StreamHandler", + }, }, 'loggers': { 'django': { @@ -381,7 +384,12 @@ def portal_filter(record): 'console', ], 'level': 'INFO' - } + }, + "csp_reports": { + "handlers": ["csp_file"], + "level": "WARNING", + "propagate": False, + }, }, } diff --git a/server/portal/urls.py b/server/portal/urls.py index c7586c36d9..16bb9cbfee 100644 --- a/server/portal/urls.py +++ b/server/portal/urls.py @@ -28,8 +28,8 @@ from django.views.generic.base import TemplateView from django.urls import path, re_path, include from impersonate import views as impersonate_views -from portal.views.views import health_check -from portal.views.views import serve_docs +from portal.views.views import health_check, serve_docs, csp_report + admin.autodiscover() urlpatterns = [ @@ -106,7 +106,7 @@ path('request-access/', include('portal.apps.request_access.urls', namespace='request_access')), path('user-news/', include('portal.apps.news.urls', namespace='news')), path('search/', include('portal.apps.site_search.urls', namespace='site_search')), - + path("csp-report/", csp_report, name="csp-report"), # portal_messages path('api/portal_messages/', include('portal.apps.portal_messages.urls', namespace='portal_messages')), diff --git a/server/portal/views/views.py b/server/portal/views/views.py index 795a481887..783f1adc1e 100644 --- a/server/portal/views/views.py +++ b/server/portal/views/views.py @@ -4,9 +4,28 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.http import HttpResponse, JsonResponse, Http404 +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_POST +import json logger = logging.getLogger(__name__) +@csrf_exempt +@require_POST +def csp_report(request): + logger = logging.getLogger("csp_reports") + + try: + payload = json.loads(request.body.decode("utf-8")) + except (ValueError, UnicodeDecodeError): + logger.warning("Malformed CSP report body: %r", request.body[:500]) + return HttpResponse(status=204) + + report = payload.get("csp-report", payload) + + logger.warning("CSP violation: %s", json.dumps(report)) + + return HttpResponse(status=204) def project_version(request): try: