From f704cb4b7d8a2fac5ee338555c5ad64201b57ca3 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Mon, 24 Jul 2023 11:11:58 +0000 Subject: [PATCH] Bug 1839165 - Throttle the number of CSP reports that are send. r=freddyb Differential Revision: https://phabricator.services.mozilla.com/D181392 --- .../en-US/chrome/security/csp.properties | 1 + dom/security/nsCSPContext.cpp | 55 +++++++++++++++++-- dom/security/nsCSPContext.h | 8 +++ modules/libpref/init/StaticPrefList.yaml | 12 ++++ ...t_ext_contentscript_triggeringPrincipal.js | 3 + 5 files changed, 75 insertions(+), 4 deletions(-) diff --git a/dom/locales/en-US/chrome/security/csp.properties b/dom/locales/en-US/chrome/security/csp.properties index b8c2700fe846..5fc7bcfdf575 100644 --- a/dom/locales/en-US/chrome/security/csp.properties +++ b/dom/locales/en-US/chrome/security/csp.properties @@ -20,6 +20,7 @@ CSPROViolationWithURI = The page’s settings observed the loading of a resource # LOCALIZATION NOTE (triedToSendReport): # %1$S is the URI we attempted to send a report to. triedToSendReport = Tried to send report to invalid URI: “%1$S” +tooManyReports = Prevented too many CSP reports from being sent within a short period of time. # LOCALIZATION NOTE (couldNotParseReportURI): # %1$S is the report URI that could not be parsed couldNotParseReportURI = couldn’t parse report URI: %1$S diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 26abac69f416..bea8da14ee48 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -1147,12 +1147,63 @@ nsresult nsCSPContext::GatherSecurityPolicyViolationEventData( return NS_OK; } +bool nsCSPContext::ShouldThrottleReport( + const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) { + // Fetch rate limiting preferences + const uint32_t kLimitCount = + StaticPrefs::security_csp_reporting_limit_count(); + const uint32_t kTimeSpanSeconds = + StaticPrefs::security_csp_reporting_limit_timespan(); + + // Disable throttling if either of the preferences is set to 0. + if (kLimitCount == 0 || kTimeSpanSeconds == 0) { + return false; + } + + TimeDuration throttleSpan = TimeDuration::FromSeconds(kTimeSpanSeconds); + if (mSendReportLimitSpanStart.IsNull() || + ((TimeStamp::Now() - mSendReportLimitSpanStart) > throttleSpan)) { + // Initial call or timespan exceeded, reset counter and timespan. + mSendReportLimitSpanStart = TimeStamp::Now(); + mSendReportLimitCount = 1; + // Also make sure we warn about omitted messages. (XXX or only do this once + // per context?) + mWarnedAboutTooManyReports = false; + return false; + } + + if (mSendReportLimitCount < kLimitCount) { + mSendReportLimitCount++; + return false; + } + + // Rate limit reached + if (!mWarnedAboutTooManyReports) { + logToConsole("tooManyReports", {}, aViolationEventInit.mSourceFile, + aViolationEventInit.mSample, aViolationEventInit.mLineNumber, + aViolationEventInit.mColumnNumber, nsIScriptError::errorFlag); + mWarnedAboutTooManyReports = true; + } + return true; +} + nsresult nsCSPContext::SendReports( const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit, uint32_t aViolatedPolicyIndex) { EnsureIPCPoliciesRead(); NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1); + nsTArray reportURIs; + mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs); + // There is nowhere to send reports to. + if (reportURIs.IsEmpty()) { + return NS_OK; + } + + if (ShouldThrottleReport(aViolationEventInit)) { + return NS_OK; + } + dom::CSPReport report; // blocked-uri @@ -1211,10 +1262,6 @@ nsresult nsCSPContext::SendReports( } // ---------- Assembled, now send it to all the report URIs ----------- // - - nsTArray reportURIs; - mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs); - nsCOMPtr doc = do_QueryReferent(mLoadingContext); nsCOMPtr reportURI; nsCOMPtr reportChannel; diff --git a/dom/security/nsCSPContext.h b/dom/security/nsCSPContext.h index 97c01385d66f..115fe781495b 100644 --- a/dom/security/nsCSPContext.h +++ b/dom/security/nsCSPContext.h @@ -151,6 +151,10 @@ class nsCSPContext : public nsIContentSecurityPolicy { private: void EnsureIPCPoliciesRead(); + bool ShouldThrottleReport( + const mozilla::dom::SecurityPolicyViolationEventInit& + aViolationEventInit); + bool permitsInternal(CSPDirective aDir, mozilla::dom::Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener, @@ -192,6 +196,10 @@ class nsCSPContext : public nsIContentSecurityPolicy { nsTArray mConsoleMsgQueue; bool mQueueUpMessages; nsCOMPtr mEventTarget; + + mozilla::TimeStamp mSendReportLimitSpanStart; + uint32_t mSendReportLimitCount = 1; + bool mWarnedAboutTooManyReports = false; }; // Class that listens to violation report transmission and logs errors. diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 7df5f787855e..e8c9be98d798 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -13489,6 +13489,18 @@ value: true mirror: always +# Limit the number of CSP reports that are send in a specific timespan. +- name: security.csp.reporting.limit.count + type: uint32_t + value: 100 + mirror: always + +# Time span in seconds for reporting limit. +- name: security.csp.reporting.limit.timespan + type: uint32_t + value: 2 + mirror: always + # If true, all toplevel data: URI navigations will be blocked. # Please note that manually entering a data: URI in the # URL-Bar will not be blocked when flipping this pref. diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js index 115fa77cc598..7115a7b8955f 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js @@ -21,6 +21,9 @@ Services.prefs.setIntPref( 4096 ); +// Do not limit the number of CSP reports. +Services.prefs.setIntPref("security.csp.reporting.limit.count", 0); + // Do not trunacate the blocked-uri in CSP reports for frame navigations. Services.prefs.setBoolPref( "security.csp.truncate_blocked_uri_for_frame_navigations",