From 8876b07abde0c8d2a4974f79b60562b6d0193db9 Mon Sep 17 00:00:00 2001 From: Wenchong Hu <173964158+whu-lyft@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:31:27 -0700 Subject: [PATCH] Prevent XSS from API call (#436) ### Description: Prevent XSS ### Test test in confidant-staging --- CHANGELOG.md | 4 ++++ VERSION | 2 +- confidant/routes/credentials.py | 7 +++++++ confidant/routes/services.py | 7 +++++++ confidant/utils/misc.py | 27 +++++++++++++++++++++++++++ docker-compose.yml | 1 - 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f783a3..40708274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 6.6.2 + +* XSS security fix / enhancement for Flask API response + ## 6.6.1 * Upgrade confidant to python 3.10.14 diff --git a/VERSION b/VERSION index 09a7391e..28179fc1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.6.1 +6.6.2 diff --git a/confidant/routes/credentials.py b/confidant/routes/credentials.py index 023131c5..63cde87d 100644 --- a/confidant/routes/credentials.py +++ b/confidant/routes/credentials.py @@ -36,6 +36,7 @@ @blueprint.route('/v1/credentials', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_credential_list(): """ @@ -132,6 +133,7 @@ def get_credential_list(): @blueprint.route('/v1/credentials/', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_credential(id): """ @@ -369,6 +371,7 @@ def diff_credential(id, old_revision, new_revision): @blueprint.route('/v1/archive/credentials/', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_archive_credential_revisions(id): """ @@ -451,6 +454,7 @@ def get_archive_credential_revisions(id): @blueprint.route('/v1/archive/credentials', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_archive_credential_list(): """ @@ -534,6 +538,7 @@ def get_archive_credential_list(): @blueprint.route('/v1/credentials', methods=['POST']) +@misc.prevent_xss_decorator @authnz.require_auth @authnz.require_csrf_token @maintenance.check_maintenance_mode @@ -727,6 +732,7 @@ def get_credential_dependencies(id): @blueprint.route('/v1/credentials/', methods=['PUT']) +@misc.prevent_xss_decorator @authnz.require_auth @authnz.require_csrf_token @maintenance.check_maintenance_mode @@ -952,6 +958,7 @@ def update_credential(id): @blueprint.route('/v1/credentials//', methods=['PUT']) +@misc.prevent_xss_decorator @authnz.require_auth @authnz.require_csrf_token @maintenance.check_maintenance_mode diff --git a/confidant/routes/services.py b/confidant/routes/services.py index 9775f634..265ef90d 100644 --- a/confidant/routes/services.py +++ b/confidant/routes/services.py @@ -76,6 +76,7 @@ def get_iam_roles_list(): @blueprint.route('/v1/services', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_service_list(): """ @@ -164,10 +165,12 @@ def get_service_list(): services_response = ServicesResponse.from_services( Service.data_type_date_index.query('service'), ) + return services_response_schema.dumps(services_response) @blueprint.route('/v1/services/', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_service(id): ''' @@ -334,6 +337,7 @@ def get_service(id): @blueprint.route('/v1/archive/services/', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_archive_service_revisions(id): """ @@ -414,6 +418,7 @@ def get_archive_service_revisions(id): @blueprint.route('/v1/archive/services', methods=['GET']) +@misc.prevent_xss_decorator @authnz.require_auth def get_archive_service_list(): """ @@ -492,6 +497,7 @@ def get_archive_service_list(): @blueprint.route('/v1/services/', methods=['PUT']) +@misc.prevent_xss_decorator @authnz.require_auth @authnz.require_csrf_token @maintenance.check_maintenance_mode @@ -701,6 +707,7 @@ def map_service_credentials(id): @blueprint.route('/v1/services//', methods=['PUT']) +@misc.prevent_xss_decorator @authnz.require_auth @authnz.require_csrf_token @maintenance.check_maintenance_mode diff --git a/confidant/utils/misc.py b/confidant/utils/misc.py index 3bf320bf..c96a8402 100644 --- a/confidant/utils/misc.py +++ b/confidant/utils/misc.py @@ -1,6 +1,8 @@ +from functools import wraps import importlib import pytz from datetime import datetime +from flask import make_response def dict_deep_update(a, b): @@ -54,3 +56,28 @@ def utcnow(): """ now = datetime.utcnow() return now.replace(tzinfo=pytz.utc) + + +def prevent_xss_decorator(func): + """ + Prevents XSS attacks: + 1. Set content type to be application/json + 2. Set Content Security Policy (already specified at app level) + 3. Enable XSS Protection + 4. Prevent MIME Type Sniffing + 5. Limit Referrer Information + """ + @wraps(func) + def wrapper(*args, **kwargs): + # Call the original function to get the response + pre_xss_response = func(*args, **kwargs) + + # Apply XSS prevention + response = make_response(pre_xss_response) + response.headers['Content-Type'] = 'application/json' + response.headers['X-XSS-Protection'] = '1; mode=block' + response.headers['X-Content-Type-Options'] = 'nosniff' + response.headers['Referrer-Policy'] = 'no-referrer' + + return response + return wrapper diff --git a/docker-compose.yml b/docker-compose.yml index 3b963335..8196051a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.8" networks: confidant: name: confidant