feat: log an encrypted string of the full cookie header when over threshold (#29735)
* feat: log an encrypted string of the full cookie header when over threshold
This commit is contained in:
@@ -14,6 +14,7 @@ from edx_toggles.toggles import WaffleFlag
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework.views import exception_handler
|
||||
from common.djangoapps.util.log_sensitive import encrypt_for_log
|
||||
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
|
||||
@@ -143,11 +144,37 @@ class CookieMonitoringMiddleware(MiddlewareMixin):
|
||||
- `request_utils.capture_cookie_sizes`
|
||||
- TOP_N_COOKIES_CAPTURED
|
||||
- TOP_N_COOKIE_GROUPS_CAPTURED
|
||||
- COOKIE_SIZE_LOGGING_THRESHOLD
|
||||
- COOKIE_HEADER_DEBUG_PUBLIC_KEY
|
||||
|
||||
"""
|
||||
|
||||
raw_header_cookie = request.META.get('HTTP_COOKIE', '')
|
||||
set_custom_attribute('cookies.header.size', len(raw_header_cookie.encode('utf-8')))
|
||||
cookie_header_size = len(raw_header_cookie.encode('utf-8'))
|
||||
set_custom_attribute('cookies.header.size', cookie_header_size)
|
||||
|
||||
# .. setting_name: COOKIE_SIZE_LOGGING_THRESHOLD
|
||||
# .. setting_default: None
|
||||
# .. setting_description: The minimum size for logging the entire (encrypted) cookie header. Should be set
|
||||
# to a relatively high threshold (suggested 9-10K) to avoid flooding the logs.
|
||||
# .. setting_warning: Requires COOKIE_HEADER_DEBUG_PUBLIC_KEY to be set
|
||||
logging_threshold = getattr(settings, "COOKIE_SIZE_LOGGING_THRESHOLD", None)
|
||||
|
||||
# .. setting_name: COOKIE_HEADER_DEBUG_PUBLIC_KEY
|
||||
# .. setting_default: None
|
||||
# .. setting_description: The public key used to encrypt large cookie headers. See See
|
||||
# https://github.com/edx/edx-platform/blob/master/common/djangoapps/util/log_sensitive.py
|
||||
# for instructions on decrypting.
|
||||
debug_key = getattr(settings, "COOKIE_HEADER_DEBUG_PUBLIC_KEY", None)
|
||||
|
||||
if logging_threshold and cookie_header_size > logging_threshold:
|
||||
if not debug_key:
|
||||
log.warning("COOKIE_SIZE_LOGGING_THRESHOLD set without COOKIE_HEADER_DEBUG_PUBLIC_KEY")
|
||||
else:
|
||||
encrypted_cookie_header = encrypt_for_log(str(raw_header_cookie),
|
||||
debug_key)
|
||||
log.info(f"Large (> {logging_threshold}) cookie header detected."
|
||||
f" Encrypted contents: {encrypted_cookie_header}")
|
||||
|
||||
if not CAPTURE_COOKIE_SIZES.is_enabled():
|
||||
return
|
||||
|
||||
@@ -267,6 +267,51 @@ class RequestUtilTestCase(unittest.TestCase):
|
||||
call('cookies_total_size', 25),
|
||||
], any_order=True)
|
||||
|
||||
@override_settings(COOKIE_SIZE_LOGGING_THRESHOLD=1)
|
||||
@patch("openedx.core.lib.request_utils.CAPTURE_COOKIE_SIZES")
|
||||
@patch("openedx.core.lib.request_utils.encrypt_for_log")
|
||||
def test_log_encrypted_cookies_no_key(self, mock_encrypt, mock_capture_cookie_sizes):
|
||||
middleware = CookieMonitoringMiddleware()
|
||||
|
||||
cookies_dict = {
|
||||
"a": "." * 10,
|
||||
"b": "." * 15,
|
||||
}
|
||||
|
||||
factory = RequestFactory()
|
||||
for name, value in cookies_dict.items():
|
||||
factory.cookies[name] = value
|
||||
|
||||
mock_request = factory.request()
|
||||
|
||||
middleware.process_request(mock_request)
|
||||
mock_encrypt.assert_not_called()
|
||||
|
||||
@override_settings(COOKIE_SIZE_LOGGING_THRESHOLD=1)
|
||||
@override_settings(COOKIE_HEADER_DEBUG_PUBLIC_KEY="fake-key")
|
||||
@patch("openedx.core.lib.request_utils.CAPTURE_COOKIE_SIZES")
|
||||
@patch("openedx.core.lib.request_utils.encrypt_for_log")
|
||||
def test_log_encrypted_cookies(self, mock_encrypt, mock_capture_cookie_sizes):
|
||||
|
||||
middleware = CookieMonitoringMiddleware()
|
||||
|
||||
cookies_dict = {
|
||||
"a": "." * 10,
|
||||
"b": "." * 15,
|
||||
}
|
||||
|
||||
factory = RequestFactory()
|
||||
for name, value in cookies_dict.items():
|
||||
factory.cookies[name] = value
|
||||
|
||||
mock_request = factory.request()
|
||||
cookie_header = str(mock_request.META.get('HTTP_COOKIE', ''))
|
||||
|
||||
middleware.process_request(mock_request)
|
||||
mock_encrypt.assert_has_calls([
|
||||
call(cookie_header, "fake-key")
|
||||
])
|
||||
|
||||
|
||||
class TestGetExpectedErrorSettingsDict(unittest.TestCase):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user