Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: content/browser/frame_host/ancestor_throttle.cc

Issue 1617043002: Introduce AncestorThrottle, which will process 'X-Frame-Options' headers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@block-response
Patch Set: Test+ErrorPage Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/frame_host/ancestor_throttle.h"
6
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "content/browser/frame_host/frame_tree.h"
11 #include "content/browser/frame_host/frame_tree_node.h"
12 #include "content/browser/frame_host/navigation_handle_impl.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/navigation_handle.h"
15 #include "content/public/browser/navigation_throttle.h"
16 #include "content/public/common/console_message_level.h"
17 #include "net/http/http_response_headers.h"
18 #include "url/origin.h"
19
20 namespace content {
21
22 // static
23 std::unique_ptr<NavigationThrottle> AncestorThrottle::MaybeCreateThrottleFor(
24 NavigationHandle* handle) {
25 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
26
27 if (handle->IsInMainFrame())
28 return nullptr;
29
30 return std::unique_ptr<NavigationThrottle>(new AncestorThrottle(handle));
31 }
32
33 AncestorThrottle::AncestorThrottle(NavigationHandle* handle)
34 : NavigationThrottle(handle) {}
35
36 AncestorThrottle::~AncestorThrottle() {}
37
38 NavigationThrottle::ThrottleCheckResult
39 AncestorThrottle::WillProcessResponse() {
40 DCHECK(!navigation_handle()->IsInMainFrame());
41
42 NavigationHandleImpl* handle =
43 static_cast<NavigationHandleImpl*>(navigation_handle());
44
45 std::string header_value;
46 HeaderDisposition disposition =
47 ParseHeader(handle->GetResponseHeaders(), &header_value);
48 switch (disposition) {
49 case CONFLICT:
50 ParseError(header_value, disposition);
51 return NavigationThrottle::BLOCK_RESPONSE;
52
53 case INVALID:
54 ParseError(header_value, disposition);
55 // TODO(mkwst): Consider failing here.
56 return NavigationThrottle::PROCEED;
57
58 case DENY:
59 ConsoleError(disposition);
60 return NavigationThrottle::BLOCK_RESPONSE;
61
62 case SAMEORIGIN: {
63 url::Origin current_origin(navigation_handle()->GetURL());
64 url::Origin top_origin =
65 handle->frame_tree_node()->frame_tree()->root()->current_origin();
66 if (top_origin.IsSameOriginWith(current_origin))
67 return NavigationThrottle::PROCEED;
68 ConsoleError(disposition);
69 return NavigationThrottle::BLOCK_RESPONSE;
70 }
71
72 case NONE:
73 case IGNORE:
74 case ALLOWALL:
75 return NavigationThrottle::PROCEED;
76 }
77 NOTREACHED();
78 return NavigationThrottle::BLOCK_RESPONSE;
79 }
80
81 void AncestorThrottle::ParseError(const std::string& value,
82 HeaderDisposition disposition) {
83 DCHECK(disposition == CONFLICT || disposition == INVALID);
84
85 std::string message;
86 if (disposition == CONFLICT) {
87 message = base::StringPrintf(
88 "Refused to display '%s' in a frame because it set multiple "
89 "'X-Frame-Options' headers with conflicting values "
90 "('%s'). Falling back to 'deny'.",
91 navigation_handle()->GetURL().spec().c_str(), value.c_str());
92 } else {
93 message = base::StringPrintf(
94 "Invalid 'X-Frame-Options' header encountered when loading '%s': "
95 "'%s' is not a recognized directive. The header will be ignored.",
96 navigation_handle()->GetURL().spec().c_str(), value.c_str());
97 }
98
99 // Log a console error in the parent of the current RenderFrameHost (as
100 // the current RenderFrameHost itself doesn't yet have a document).
101 navigation_handle()->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
102 CONSOLE_MESSAGE_LEVEL_ERROR, message);
103 }
104
105 void AncestorThrottle::ConsoleError(HeaderDisposition disposition) {
106 DCHECK(disposition == DENY || disposition == SAMEORIGIN);
107 std::string message = base::StringPrintf(
108 "Refused to display '%s' in a frame because it set 'X-Frame-Options' "
109 "to '%s'.",
110 navigation_handle()->GetURL().spec().c_str(),
111 disposition == DENY ? "deny" : "sameorigin");
112
113 // Log a console error in the parent of the current RenderFrameHost (as
114 // the current RenderFrameHost itself doesn't yet have a document).
115 navigation_handle()->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
116 CONSOLE_MESSAGE_LEVEL_ERROR, message);
117 }
118
119 AncestorThrottle::HeaderDisposition AncestorThrottle::ParseHeader(
120 const net::HttpResponseHeaders* headers,
121 std::string* header_value) {
122 DCHECK(header_value);
123 if (!headers)
124 return NONE;
125
126 HeaderDisposition result = NONE;
127 size_t iter = 0;
128 std::string value;
129 while (headers->EnumerateHeader(&iter, "x-frame-options", &value)) {
mmenke 2016/05/02 18:17:28 Is there a w3c doc we could link for this, too?
Mike West 2016/05/05 08:14:28 Sure! Linked to RFC7034.
130 HeaderDisposition current = INVALID;
131
132 base::StringPiece trimmed =
133 base::TrimWhitespaceASCII(value, base::TRIM_ALL);
134 if (!header_value->empty())
135 header_value->append(", ");
136 header_value->append(trimmed.as_string());
137
138 if (base::LowerCaseEqualsASCII(trimmed, "deny"))
139 current = DENY;
140 else if (base::LowerCaseEqualsASCII(trimmed, "allowall"))
141 current = ALLOWALL;
142 else if (base::LowerCaseEqualsASCII(trimmed, "sameorigin"))
143 current = SAMEORIGIN;
144 else
145 current = INVALID;
146
147 if (result == NONE)
148 result = current;
149 else if (result != current)
150 result = CONFLICT;
151 }
152
153 // If 'X-Frame-Options' would potentially block the response, check whether
154 // the 'frame-ancestors' CSP directive should take effect instead. See
155 // https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
156 if (result != NONE && result != ALLOWALL) {
157 iter = 0;
158 value = std::string();
159 while (headers->EnumerateHeader(&iter, "content-security-policy", &value)) {
160 // TODO(mkwst): 'frame-ancestors' is currently handled in Blink. We should
161 // handle it here instead. Until then, don't block the request, and let
162 // Blink handle it. https://crbug.com/555418
163 std::vector<std::string> tokens = base::SplitString(
164 value, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
165 if (std::count_if(tokens.begin(), tokens.end(), [](std::string token) {
166 // The trailing " " is intentional; we'd otherwise match
167 // "frame-ancestors-is-not-this-directive".
168 return base::StartsWith(token, "frame-ancestors ",
169 base::CompareCase::INSENSITIVE_ASCII);
170 })) {
171 return IGNORE;
172 }
173 }
174 }
175 return result;
176 }
177
178 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698