Commit d1b55480 authored by Antonio Sartori's avatar Antonio Sartori Committed by Commit Bot

Avoid reparsing Content-Security-Policy headers in AncestorThrottle

The logic checking the X-Frame-Options header in the AncestorThrottle
was manually parsing the Content-Security-Policy headers for finding
out whether the 'frame-ancestors' directive was used (since the
X-Frame-Options header should be ignored if the 'frame-ancestors' CSP
directive is being used).

With this change we use instead the already parsed
Content-Security-Policy in parsed_headers for checking that. This
avoids unnecessary additional parsings of the CSP headers in the
AncestorThrottle.

Bug: 1032139
Change-Id: Ibe0c64cd7eb1e6f8ff0fa915a81db6248399da1e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2292051Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Cr-Commit-Position: refs/heads/master@{#789919}
parent 1367055f
...@@ -49,6 +49,7 @@ jumbo_source_set("browser") { ...@@ -49,6 +49,7 @@ jumbo_source_set("browser") {
"//base:base_static", "//base:base_static",
"//base:clang_profiling_buildflags", "//base:clang_profiling_buildflags",
"//base/third_party/dynamic_annotations", "//base/third_party/dynamic_annotations",
"//base/util/ranges:ranges",
"//build:branding_buildflags", "//build:branding_buildflags",
"//build:lacros_buildflags", "//build:lacros_buildflags",
"//cc", "//cc",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/util/ranges/algorithm.h"
#include "content/browser/frame_host/frame_tree.h" #include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigation_request.h" #include "content/browser/frame_host/navigation_request.h"
...@@ -79,27 +80,15 @@ void RecordXFrameOptionsUsage(XFrameOptionsHistogram usage) { ...@@ -79,27 +80,15 @@ void RecordXFrameOptionsUsage(XFrameOptionsHistogram usage) {
XFrameOptionsHistogram::XFRAMEOPTIONS_HISTOGRAM_MAX); XFrameOptionsHistogram::XFRAMEOPTIONS_HISTOGRAM_MAX);
} }
bool HeadersContainFrameAncestorsCSP(const net::HttpResponseHeaders* headers, bool HeadersContainFrameAncestorsCSP(
bool include_report_only) { const network::mojom::ParsedHeadersPtr& headers) {
std::vector<std::string> header_names = {"content-security-policy"}; return util::ranges::any_of(
if (include_report_only) headers->content_security_policy, [](const auto& csp) {
header_names.push_back("content-security-policy-report-only"); return csp->header->type ==
for (const auto& header : header_names) { network::mojom::ContentSecurityPolicyType::kEnforce &&
size_t iter = 0; csp->directives.count(
std::string value; network::mojom::CSPDirectiveName::FrameAncestors);
while (headers->EnumerateHeader(&iter, header, &value)) { });
// A content-security-policy is a semicolon-separated list of directives.
for (const auto& directive : base::SplitStringPiece(
value, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
// The trailing " " is intentional; we'd otherwise match
// "frame-ancestors-is-not-this-directive".
if (base::StartsWith(directive, "frame-ancestors ",
base::CompareCase::INSENSITIVE_ASCII))
return true;
}
}
}
return false;
} }
class FrameAncestorCSPContext : public network::CSPContext { class FrameAncestorCSPContext : public network::CSPContext {
...@@ -285,6 +274,16 @@ AncestorThrottle::CheckResult AncestorThrottle::EvaluateXFrameOptions( ...@@ -285,6 +274,16 @@ AncestorThrottle::CheckResult AncestorThrottle::EvaluateXFrameOptions(
HeaderDisposition disposition = HeaderDisposition disposition =
ParseXFrameOptionsHeader(request->GetResponseHeaders(), &header_value); ParseXFrameOptionsHeader(request->GetResponseHeaders(), &header_value);
// If 'X-Frame-Options' would potentially block the response, check whether
// the 'frame-ancestors' CSP directive should take effect instead. See
// https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
if (disposition != HeaderDisposition::NONE &&
disposition != HeaderDisposition::ALLOWALL &&
HeadersContainFrameAncestorsCSP(request->response()->parsed_headers)) {
RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
return CheckResult::PROCEED;
}
switch (disposition) { switch (disposition) {
case HeaderDisposition::CONFLICT: case HeaderDisposition::CONFLICT:
if (logging == LoggingDisposition::LOG_TO_CONSOLE) if (logging == LoggingDisposition::LOG_TO_CONSOLE)
...@@ -339,9 +338,6 @@ AncestorThrottle::CheckResult AncestorThrottle::EvaluateXFrameOptions( ...@@ -339,9 +338,6 @@ AncestorThrottle::CheckResult AncestorThrottle::EvaluateXFrameOptions(
case HeaderDisposition::NONE: case HeaderDisposition::NONE:
RecordXFrameOptionsUsage(XFrameOptionsHistogram::NONE); RecordXFrameOptionsUsage(XFrameOptionsHistogram::NONE);
return CheckResult::PROCEED; return CheckResult::PROCEED;
case HeaderDisposition::BYPASS:
RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
return CheckResult::PROCEED;
case HeaderDisposition::ALLOWALL: case HeaderDisposition::ALLOWALL:
RecordXFrameOptionsUsage(XFrameOptionsHistogram::ALLOWALL); RecordXFrameOptionsUsage(XFrameOptionsHistogram::ALLOWALL);
return CheckResult::PROCEED; return CheckResult::PROCEED;
...@@ -524,18 +520,6 @@ AncestorThrottle::HeaderDisposition AncestorThrottle::ParseXFrameOptionsHeader( ...@@ -524,18 +520,6 @@ AncestorThrottle::HeaderDisposition AncestorThrottle::ParseXFrameOptionsHeader(
result = HeaderDisposition::CONFLICT; result = HeaderDisposition::CONFLICT;
} }
// If 'X-Frame-Options' would potentially block the response, check whether
// the 'frame-ancestors' CSP directive should take effect instead. See
// https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
if (result != HeaderDisposition::NONE &&
result != HeaderDisposition::ALLOWALL &&
// TODO(antoniosartori): Use the already parsed CSP header instead of the
// raw headers here as soon as we remove
// network::features::kOutOfBlinkFrameAncestors
HeadersContainFrameAncestorsCSP(headers, false)) {
return HeaderDisposition::BYPASS;
}
return result; return result;
} }
......
...@@ -37,8 +37,7 @@ class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle { ...@@ -37,8 +37,7 @@ class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle {
SAMEORIGIN, SAMEORIGIN,
ALLOWALL, ALLOWALL,
INVALID, INVALID,
CONFLICT, CONFLICT
BYPASS
}; };
static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor( static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment