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

Side by Side Diff: content/common/content_security_policy/csp_source.cc

Issue 2792013002: Stop CSP from matching independent scheme/port upgrades (content layer) (Closed)
Patch Set: Format changes Created 3 years, 8 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
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <sstream> 5 #include <sstream>
6 6
7 #include "base/strings/string_util.h" 7 #include "base/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "content/common/content_security_policy/csp_context.h" 9 #include "content/common/content_security_policy/csp_context.h"
10 #include "url/url_canon.h" 10 #include "url/url_canon.h"
11 #include "url/url_util.h" 11 #include "url/url_util.h"
12 12
13 namespace content { 13 namespace content {
14 14
15 namespace { 15 namespace {
16 16
17 bool DecodePath(const base::StringPiece& path, std::string* output) { 17 bool DecodePath(const base::StringPiece& path, std::string* output) {
18 url::RawCanonOutputT<base::char16> unescaped; 18 url::RawCanonOutputT<base::char16> unescaped;
19 url::DecodeURLEscapeSequences(path.data(), path.size(), &unescaped); 19 url::DecodeURLEscapeSequences(path.data(), path.size(), &unescaped);
20 return base::UTF16ToUTF8(unescaped.data(), unescaped.length(), output); 20 return base::UTF16ToUTF8(unescaped.data(), unescaped.length(), output);
21 } 21 }
22 22
23 int DefaultPortForScheme(const std::string& scheme) { 23 int DefaultPortForScheme(const std::string& scheme) {
24 return url::DefaultPortForScheme(scheme.data(), scheme.size()); 24 return url::DefaultPortForScheme(scheme.data(), scheme.size());
25 } 25 }
26 26
27 bool SourceAllowScheme(const CSPSource& source, 27 // NotMatching is the only negative member, the rest are different types of
28 const GURL& url, 28 // matches. NotMatching should always be 0 to let if statements work nicely
29 CSPContext* context) { 29 enum class PortMatchingResult {
30 if (source.scheme.empty()) 30 NotMatching,
31 return context->ProtocolMatchesSelf(url); 31 MatchingWildcard,
32 if (source.scheme == url::kHttpScheme) 32 MatchingUpgrade,
33 return url.SchemeIsHTTPOrHTTPS(); 33 MatchingExact
34 if (source.scheme == url::kWsScheme) 34 };
35 return url.SchemeIsWSOrWSS(); 35 enum class SchemeMatchingResult { NotMatching, MatchingUpgrade, MatchingExact };
36 return url.SchemeIs(source.scheme); 36
37 SchemeMatchingResult SourceAllowScheme(const CSPSource& source,
38 const GURL& url,
39 CSPContext* context) {
40 const std::string& source_scheme =
41 source.scheme.empty() ? context->GetSelfScheme() : source.scheme;
42
43 if (source_scheme.empty()) {
44 if (context->ProtocolIsSelf(url))
45 return SchemeMatchingResult::MatchingExact;
46 return SchemeMatchingResult::NotMatching;
47 }
48
49 if (url.SchemeIs(source_scheme))
50 return SchemeMatchingResult::MatchingExact;
51
52 if ((source_scheme == url::kHttpScheme && url.SchemeIs(url::kHttpsScheme)) ||
53 (source_scheme == url::kHttpScheme &&
54 url.SchemeIs(url::kHttpsSuboriginScheme)) ||
55 (source_scheme == url::kWsScheme && url.SchemeIs(url::kWssScheme))) {
56 return SchemeMatchingResult::MatchingUpgrade;
57 }
58
59 if ((source_scheme == url::kHttpScheme &&
60 url.SchemeIs(url::kHttpSuboriginScheme)) ||
61 (source_scheme == url::kHttpsScheme &&
62 url.SchemeIs(url::kHttpsSuboriginScheme))) {
63 return SchemeMatchingResult::MatchingExact;
64 }
65
66 return SchemeMatchingResult::NotMatching;
37 } 67 }
38 68
39 bool SourceAllowHost(const CSPSource& source, const GURL& url) { 69 bool SourceAllowHost(const CSPSource& source, const GURL& url) {
40 if (source.is_host_wildcard) { 70 if (source.is_host_wildcard) {
41 if (source.host.empty()) 71 if (source.host.empty())
42 return true; 72 return true;
43 // TODO(arthursonzogni): Chrome used to, incorrectly, match *.x.y to x.y. 73 // TODO(arthursonzogni): Chrome used to, incorrectly, match *.x.y to x.y.
44 // The renderer version of this function count how many times it happens. 74 // The renderer version of this function count how many times it happens.
45 // It might be useful to do it outside of blink too. 75 // It might be useful to do it outside of blink too.
46 // See third_party/WebKit/Source/core/frame/csp/CSPSource.cpp 76 // See third_party/WebKit/Source/core/frame/csp/CSPSource.cpp
47 return base::EndsWith(url.host(), '.' + source.host, 77 return base::EndsWith(url.host(), '.' + source.host,
48 base::CompareCase::INSENSITIVE_ASCII); 78 base::CompareCase::INSENSITIVE_ASCII);
49 } else 79 } else
50 return url.host() == source.host; 80 return url.host() == source.host;
51 } 81 }
52 82
53 bool SourceAllowPort(const CSPSource& source, const GURL& url) { 83 PortMatchingResult SourceAllowPort(const CSPSource& source, const GURL& url) {
54 int url_port = url.EffectiveIntPort(); 84 int url_port = url.EffectiveIntPort();
55 85
56 if (source.is_port_wildcard) 86 if (source.is_port_wildcard)
57 return true; 87 return PortMatchingResult::MatchingWildcard;
58 88
59 if (source.port == url::PORT_UNSPECIFIED) 89 if (source.port == url_port) {
60 return DefaultPortForScheme(url.scheme()) == url_port; 90 if (source.port == url::PORT_UNSPECIFIED)
91 return PortMatchingResult::MatchingWildcard;
92 return PortMatchingResult::MatchingExact;
93 }
61 94
62 if (source.port == url_port) 95 if (source.port == url::PORT_UNSPECIFIED) {
63 return true; 96 if (DefaultPortForScheme(url.scheme()) == url_port) {
97 return PortMatchingResult::MatchingWildcard;
98 }
99 return PortMatchingResult::NotMatching;
100 }
64 101
65 if (source.port == 80 && url_port == 443) 102 int source_port = source.port;
66 return true; 103 if (source_port == url::PORT_UNSPECIFIED)
104 source_port = DefaultPortForScheme(source.scheme);
67 105
68 return false; 106 if (source_port == 80 && url_port == 443)
107 return PortMatchingResult::MatchingUpgrade;
108
109 return PortMatchingResult::NotMatching;
69 } 110 }
70 111
71 bool SourceAllowPath(const CSPSource& source, 112 bool SourceAllowPath(const CSPSource& source,
72 const GURL& url, 113 const GURL& url,
73 bool is_redirect) { 114 bool is_redirect) {
74 if (is_redirect) 115 if (is_redirect)
75 return true; 116 return true;
76 117
77 if (source.path.empty() || url.path().empty()) 118 if (source.path.empty() || url.path().empty())
78 return true; 119 return true;
79 120
80 std::string url_path; 121 std::string url_path;
81 if (!DecodePath(url.path(), &url_path)) { 122 if (!DecodePath(url.path(), &url_path)) {
82 // TODO(arthursonzogni): try to figure out if that could happen and how to 123 // TODO(arthursonzogni): try to figure out if that could happen and how to
83 // handle it. 124 // handle it.
84 return false; 125 return false;
85 } 126 }
86 127
87 // If the path represents a directory. 128 // If the path represents a directory.
88 if (base::EndsWith(source.path, "/", base::CompareCase::SENSITIVE)) 129 if (base::EndsWith(source.path, "/", base::CompareCase::SENSITIVE))
89 return base::StartsWith(url_path, source.path, 130 return base::StartsWith(url_path, source.path,
90 base::CompareCase::SENSITIVE); 131 base::CompareCase::SENSITIVE);
91 132
92 // The path represents a file. 133 // The path represents a file.
93 return source.path == url_path; 134 return source.path == url_path;
94 } 135 }
95 136
137 bool inline requiresUpgrade(const PortMatchingResult result) {
138 return result == PortMatchingResult::MatchingUpgrade;
139 }
140 bool inline requiresUpgrade(const SchemeMatchingResult result) {
141 return result == SchemeMatchingResult::MatchingUpgrade;
142 }
143 bool inline canUpgrade(const PortMatchingResult result) {
144 return result == PortMatchingResult::MatchingUpgrade ||
145 result == PortMatchingResult::MatchingWildcard;
146 }
147 bool inline canUpgrade(const SchemeMatchingResult result) {
148 return result == SchemeMatchingResult::MatchingUpgrade;
149 }
150
96 } // namespace 151 } // namespace
97 152
98 CSPSource::CSPSource() 153 CSPSource::CSPSource()
99 : scheme(), 154 : scheme(),
100 host(), 155 host(),
101 is_host_wildcard(false), 156 is_host_wildcard(false),
102 port(url::PORT_UNSPECIFIED), 157 port(url::PORT_UNSPECIFIED),
103 is_port_wildcard(false), 158 is_port_wildcard(false),
104 path() {} 159 path() {}
105 160
(...skipping 16 matching lines...) Expand all
122 177
123 CSPSource::CSPSource(const CSPSource& source) = default; 178 CSPSource::CSPSource(const CSPSource& source) = default;
124 CSPSource::~CSPSource() = default; 179 CSPSource::~CSPSource() = default;
125 180
126 // static 181 // static
127 bool CSPSource::Allow(const CSPSource& source, 182 bool CSPSource::Allow(const CSPSource& source,
128 const GURL& url, 183 const GURL& url,
129 CSPContext* context, 184 CSPContext* context,
130 bool is_redirect) { 185 bool is_redirect) {
131 if (source.IsSchemeOnly()) 186 if (source.IsSchemeOnly())
132 return SourceAllowScheme(source, url, context); 187 return SourceAllowScheme(source, url, context) !=
188 SchemeMatchingResult::NotMatching;
133 189
134 return SourceAllowScheme(source, url, context) && 190 PortMatchingResult portResult = SourceAllowPort(source, url);
135 SourceAllowHost(source, url) && SourceAllowPort(source, url) && 191 SchemeMatchingResult schemeResult = SourceAllowScheme(source, url, context);
192
193 if (requiresUpgrade(schemeResult) && !canUpgrade(portResult))
194 return false;
195 if (requiresUpgrade(portResult) && !canUpgrade(schemeResult))
196 return false;
197
198 return schemeResult != SchemeMatchingResult::NotMatching &&
199 SourceAllowHost(source, url) &&
200 portResult != PortMatchingResult::NotMatching &&
136 SourceAllowPath(source, url, is_redirect); 201 SourceAllowPath(source, url, is_redirect);
137 } 202 }
138 203
139 std::string CSPSource::ToString() const { 204 std::string CSPSource::ToString() const {
140 // scheme 205 // scheme
141 if (IsSchemeOnly()) 206 if (IsSchemeOnly())
142 return scheme + ":"; 207 return scheme + ":";
143 208
144 std::stringstream text; 209 std::stringstream text;
145 if (!scheme.empty()) 210 if (!scheme.empty())
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 242
178 bool CSPSource::HasHost() const { 243 bool CSPSource::HasHost() const {
179 return !host.empty() || is_host_wildcard; 244 return !host.empty() || is_host_wildcard;
180 } 245 }
181 246
182 bool CSPSource::HasPath() const { 247 bool CSPSource::HasPath() const {
183 return !path.empty(); 248 return !path.empty();
184 } 249 }
185 250
186 } // namespace content 251 } // namespace content
OLDNEW
« no previous file with comments | « content/common/content_security_policy/csp_context.cc ('k') | content/common/content_security_policy/csp_source_list.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698