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

Side by Side Diff: extensions/common/csp_validator.cc

Issue 481643002: Disallow non-subdomain wildcards in the extension's CSP (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: check for chrome:// and chrome-extension:// wildcards, update documentation Created 6 years, 4 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "extensions/common/csp_validator.h" 5 #include "extensions/common/csp_validator.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/strings/string_split.h" 9 #include "base/strings/string_split.h"
10 #include "base/strings/string_tokenizer.h" 10 #include "base/strings/string_tokenizer.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "content/public/common/url_constants.h" 12 #include "content/public/common/url_constants.h"
13 #include "extensions/common/constants.h" 13 #include "extensions/common/constants.h"
14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
14 15
15 namespace extensions { 16 namespace extensions {
16 17
17 namespace csp_validator { 18 namespace csp_validator {
18 19
19 namespace { 20 namespace {
20 21
21 const char kDefaultSrc[] = "default-src"; 22 const char kDefaultSrc[] = "default-src";
22 const char kScriptSrc[] = "script-src"; 23 const char kScriptSrc[] = "script-src";
23 const char kObjectSrc[] = "object-src"; 24 const char kObjectSrc[] = "object-src";
24 25
25 const char kSandboxDirectiveName[] = "sandbox"; 26 const char kSandboxDirectiveName[] = "sandbox";
26 const char kAllowSameOriginToken[] = "allow-same-origin"; 27 const char kAllowSameOriginToken[] = "allow-same-origin";
27 const char kAllowTopNavigation[] = "allow-top-navigation"; 28 const char kAllowTopNavigation[] = "allow-top-navigation";
28 29
30 const char kHttpSchemeAndSeparator[] = "https://";
31
29 struct DirectiveStatus { 32 struct DirectiveStatus {
30 explicit DirectiveStatus(const char* name) 33 explicit DirectiveStatus(const char* name)
31 : directive_name(name) 34 : directive_name(name)
32 , seen_in_policy(false) 35 , seen_in_policy(false)
33 , is_secure(false) { 36 , is_secure(false) {
34 } 37 }
35 38
36 const char* directive_name; 39 const char* directive_name;
37 bool seen_in_policy; 40 bool seen_in_policy;
38 bool is_secure; 41 bool is_secure;
39 }; 42 };
40 43
44 bool isNonWildcardScheme(const std::string& url,
45 const std::string& scheme_and_separator) {
46 if (!StartsWithASCII(url, scheme_and_separator, true))
47 return false;
48
49 // Disallow host-less schemes such as "https://".
50 if (url == scheme_and_separator)
51 return false;
52
53 const size_t scheme_and_separator_len = scheme_and_separator.length();
54 // Note: It is sufficient to only compare the first character against '*'
55 // because the CSP only allows wildcards at the start of a directive, see
56 // host-source and host-part at http://www.w3.org/TR/CSP11/#source-list-syntax
Mike West 2014/08/19 16:53:07 Nit: Can you change this link to the editor's draf
57 if (url[scheme_and_separator_len] != '*')
58 return true;
59
60 // If a wildcard is set, then it must be followed by a dot to qualify as
61 // a subdomain wildcard.
62 return url.length() > scheme_and_separator_len &&
63 url[scheme_and_separator_len + 1] == '.';
64 }
65
66 bool isHttpsUrlAllowed(const std::string& url) {
67 if (!StartsWithASCII(url, kHttpSchemeAndSeparator, true))
68 return false;
69
70 size_t start_of_host = strlen(kHttpSchemeAndSeparator);
71
72 size_t end_of_host = url.find("/", start_of_host);
73 if (end_of_host == std::string::npos)
74 end_of_host = url.size();
75
76 bool is_wildcard_subdomain = end_of_host > start_of_host + 2 &&
77 url[start_of_host] == '*' && url[start_of_host + 1] == '.';
78 if (is_wildcard_subdomain)
79 start_of_host += 2;
80
81 size_t start_of_port = url.rfind(":", end_of_host);
82 if (start_of_port != std::string::npos && start_of_port > start_of_host) {
83 bool is_valid_port = false;
84 // Do a quick sanity check. The following check could mistakenly flag
85 // ":123456" or ":****" as valid, but that does not matter because the
86 // relaxing CSP directive will just be ignored by Blink.
87 for (size_t i = start_of_port + 1; i < end_of_host; ++i) {
88 is_valid_port = IsAsciiDigit(url[i]) || url[i] == '*';
89 if (!is_valid_port)
90 break;
91 }
92 if (is_valid_port)
93 end_of_host = start_of_port;
94 }
95
96 std::string host(url, start_of_host, end_of_host - start_of_host);
97 // Global wildcards are not allowed.
98 if (host.empty() || host.find("*") != std::string::npos)
99 return false;
100
101 if (!is_wildcard_subdomain)
102 return true;
103
104 // Wildcards on subdomains of a TLD are not allowed.
105 size_t registry_length = net::registry_controlled_domains::GetRegistryLength(
106 host,
107 net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
108 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
109 return registry_length != 0;
110 }
111
41 bool HasOnlySecureTokens(base::StringTokenizer& tokenizer, 112 bool HasOnlySecureTokens(base::StringTokenizer& tokenizer,
42 Manifest::Type type) { 113 Manifest::Type type) {
43 while (tokenizer.GetNext()) { 114 while (tokenizer.GetNext()) {
44 std::string source = tokenizer.token(); 115 std::string source = tokenizer.token();
45 base::StringToLowerASCII(&source); 116 base::StringToLowerASCII(&source);
46 117
47 // Don't alow whitelisting of all hosts. This boils down to:
48 // 1. Maximum of 2 '*' characters.
49 // 2. Each '*' is either followed by a '.' or preceded by a ':'
50 int wildcards = 0;
51 size_t length = source.length();
52 for (size_t i = 0; i < length; ++i) {
53 if (source[i] == L'*') {
54 wildcards++;
55 if (wildcards > 2)
56 return false;
57
58 bool isWildcardPort = i > 0 && source[i - 1] == L':';
59 bool isWildcardSubdomain = i + 1 < length && source[i + 1] == L'.';
60 if (!isWildcardPort && !isWildcardSubdomain)
61 return false;
62 }
63 }
64
65 // We might need to relax this whitelist over time. 118 // We might need to relax this whitelist over time.
66 if (source == "'self'" || 119 if (source == "'self'" ||
67 source == "'none'" || 120 source == "'none'" ||
68 source == "http://127.0.0.1" || 121 source == "http://127.0.0.1" ||
69 LowerCaseEqualsASCII(source, "blob:") || 122 LowerCaseEqualsASCII(source, "blob:") ||
70 LowerCaseEqualsASCII(source, "filesystem:") || 123 LowerCaseEqualsASCII(source, "filesystem:") ||
71 LowerCaseEqualsASCII(source, "http://localhost") || 124 LowerCaseEqualsASCII(source, "http://localhost") ||
72 StartsWithASCII(source, "http://127.0.0.1:", false) || 125 StartsWithASCII(source, "http://127.0.0.1:", true) ||
73 StartsWithASCII(source, "http://localhost:", false) || 126 StartsWithASCII(source, "http://localhost:", true) ||
robwu 2014/08/19 16:31:39 Drive-by fix. source has already been transformed
74 StartsWithASCII(source, "https://", true) || 127 isHttpsUrlAllowed(source) ||
75 StartsWithASCII(source, "chrome://", true) || 128 isNonWildcardScheme(source, "chrome://") ||
76 StartsWithASCII(source, 129 isNonWildcardScheme(source,
77 std::string(extensions::kExtensionScheme) + 130 std::string(extensions::kExtensionScheme) +
78 url::kStandardSchemeSeparator, 131 url::kStandardSchemeSeparator) ||
79 true) ||
80 StartsWithASCII(source, "chrome-extension-resource:", true)) { 132 StartsWithASCII(source, "chrome-extension-resource:", true)) {
81 continue; 133 continue;
82 } 134 }
83 135
84 // crbug.com/146487 136 // crbug.com/146487
85 if (type == Manifest::TYPE_EXTENSION || 137 if (type == Manifest::TYPE_EXTENSION ||
86 type == Manifest::TYPE_LEGACY_PACKAGED_APP) { 138 type == Manifest::TYPE_LEGACY_PACKAGED_APP) {
87 if (source == "'unsafe-eval'") 139 if (source == "'unsafe-eval'")
88 continue; 140 continue;
89 } 141 }
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 } 250 }
199 } 251 }
200 } 252 }
201 253
202 return seen_sandbox; 254 return seen_sandbox;
203 } 255 }
204 256
205 } // namespace csp_validator 257 } // namespace csp_validator
206 258
207 } // namespace extensions 259 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698