OLD | NEW |
---|---|
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 <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... | |
21 | 21 |
22 namespace extensions { | 22 namespace extensions { |
23 | 23 |
24 namespace csp_validator { | 24 namespace csp_validator { |
25 | 25 |
26 namespace { | 26 namespace { |
27 | 27 |
28 const char kDefaultSrc[] = "default-src"; | 28 const char kDefaultSrc[] = "default-src"; |
29 const char kScriptSrc[] = "script-src"; | 29 const char kScriptSrc[] = "script-src"; |
30 const char kObjectSrc[] = "object-src"; | 30 const char kObjectSrc[] = "object-src"; |
31 const char kFrameSrc[] = "frame-src"; | |
32 const char kChildSrc[] = "child-src"; | |
33 | |
34 const char kDirectiveSeparator[] = ";"; | |
35 | |
31 const char kPluginTypes[] = "plugin-types"; | 36 const char kPluginTypes[] = "plugin-types"; |
32 | 37 |
33 const char kObjectSrcDefaultDirective[] = "object-src 'self';"; | 38 const char kObjectSrcDefaultDirective[] = "object-src 'self';"; |
34 const char kScriptSrcDefaultDirective[] = | 39 const char kScriptSrcDefaultDirective[] = |
35 "script-src 'self' chrome-extension-resource:;"; | 40 "script-src 'self' chrome-extension-resource:;"; |
36 | 41 |
42 const char kAppSandboxSubframeSrcDefaultDirective[] = "child-src 'self';"; | |
43 const char kAppSandboxScriptSrcDefaultDirective[] = "script-src 'self';"; | |
44 | |
37 const char kSandboxDirectiveName[] = "sandbox"; | 45 const char kSandboxDirectiveName[] = "sandbox"; |
38 const char kAllowSameOriginToken[] = "allow-same-origin"; | 46 const char kAllowSameOriginToken[] = "allow-same-origin"; |
39 const char kAllowTopNavigation[] = "allow-top-navigation"; | 47 const char kAllowTopNavigation[] = "allow-top-navigation"; |
40 | 48 |
41 // This is the list of plugin types which are fully sandboxed and are safe to | 49 // This is the list of plugin types which are fully sandboxed and are safe to |
42 // load up in an extension, regardless of the URL they are navigated to. | 50 // load up in an extension, regardless of the URL they are navigated to. |
43 const char* const kSandboxedPluginTypes[] = { | 51 const char* const kSandboxedPluginTypes[] = { |
44 "application/pdf", | 52 "application/pdf", |
45 "application/x-google-chrome-pdf", | 53 "application/x-google-chrome-pdf", |
46 "application/x-pnacl" | 54 "application/x-pnacl" |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
269 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == | 277 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == |
270 std::string::npos; | 278 std::string::npos; |
271 } | 279 } |
272 | 280 |
273 std::string SanitizeContentSecurityPolicy( | 281 std::string SanitizeContentSecurityPolicy( |
274 const std::string& policy, | 282 const std::string& policy, |
275 int options, | 283 int options, |
276 std::vector<InstallWarning>* warnings) { | 284 std::vector<InstallWarning>* warnings) { |
277 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | 285 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. |
278 std::vector<std::string> directives = base::SplitString( | 286 std::vector<std::string> directives = base::SplitString( |
279 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 287 policy, kDirectiveSeparator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
280 | 288 |
281 DirectiveStatus default_src_status(kDefaultSrc); | 289 DirectiveStatus default_src_status(kDefaultSrc); |
282 DirectiveStatus script_src_status(kScriptSrc); | 290 DirectiveStatus script_src_status(kScriptSrc); |
283 DirectiveStatus object_src_status(kObjectSrc); | 291 DirectiveStatus object_src_status(kObjectSrc); |
284 | 292 |
285 bool allow_insecure_object_src = | 293 bool allow_insecure_object_src = |
286 AllowedToHaveInsecureObjectSrc(options, directives); | 294 AllowedToHaveInsecureObjectSrc(options, directives); |
287 | 295 |
288 std::vector<std::string> sane_csp_parts; | 296 std::vector<std::string> sane_csp_parts; |
289 std::vector<InstallWarning> default_src_csp_warnings; | 297 std::vector<InstallWarning> default_src_csp_warnings; |
290 for (size_t i = 0; i < directives.size(); ++i) { | 298 for (size_t i = 0; i < directives.size(); ++i) { |
291 std::string& input = directives[i]; | 299 std::string& input = directives[i]; |
292 base::StringTokenizer tokenizer(input, " \t\r\n"); | 300 base::StringTokenizer tokenizer(input, " \t\r\n"); |
293 if (!tokenizer.GetNext()) | 301 if (!tokenizer.GetNext()) |
294 continue; | 302 continue; |
295 | 303 |
296 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); | 304 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); |
297 if (UpdateStatus(directive_name, &tokenizer, &default_src_status, options, | 305 if (UpdateStatus(directive_name, &tokenizer, &default_src_status, options, |
298 &sane_csp_parts, &default_src_csp_warnings)) | 306 &sane_csp_parts, &default_src_csp_warnings)) |
299 continue; | 307 continue; |
300 if (UpdateStatus(directive_name, &tokenizer, &script_src_status, options, | 308 if (UpdateStatus(directive_name, &tokenizer, &script_src_status, options, |
301 &sane_csp_parts, warnings)) | 309 &sane_csp_parts, warnings)) |
302 continue; | 310 continue; |
303 if (!allow_insecure_object_src && | 311 if (!allow_insecure_object_src && |
304 UpdateStatus(directive_name, &tokenizer, &object_src_status, options, | 312 UpdateStatus(directive_name, &tokenizer, &object_src_status, options, |
305 &sane_csp_parts, warnings)) | 313 &sane_csp_parts, warnings)) |
306 continue; | 314 continue; |
307 | 315 |
308 // Pass the other CSP directives as-is without further validation. | 316 // Pass the other CSP directives as-is without further validation. |
309 sane_csp_parts.push_back(input + ";"); | 317 sane_csp_parts.push_back(input + kDirectiveSeparator); |
310 } | 318 } |
311 | 319 |
312 if (default_src_status.seen_in_policy) { | 320 if (default_src_status.seen_in_policy) { |
313 if (!script_src_status.seen_in_policy || | 321 if (!script_src_status.seen_in_policy || |
314 !object_src_status.seen_in_policy) { | 322 !object_src_status.seen_in_policy) { |
315 // Insecure values in default-src are only relevant if either script-src | 323 // Insecure values in default-src are only relevant if either script-src |
316 // or object-src is omitted. | 324 // or object-src is omitted. |
317 if (warnings) | 325 if (warnings) |
318 warnings->insert(warnings->end(), | 326 warnings->insert(warnings->end(), |
319 default_src_csp_warnings.begin(), | 327 default_src_csp_warnings.begin(), |
(...skipping 10 matching lines...) Expand all Loading... | |
330 sane_csp_parts.push_back(kObjectSrcDefaultDirective); | 338 sane_csp_parts.push_back(kObjectSrcDefaultDirective); |
331 if (warnings) | 339 if (warnings) |
332 warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage( | 340 warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage( |
333 manifest_errors::kInvalidCSPMissingSecureSrc, kObjectSrc))); | 341 manifest_errors::kInvalidCSPMissingSecureSrc, kObjectSrc))); |
334 } | 342 } |
335 } | 343 } |
336 | 344 |
337 return base::JoinString(sane_csp_parts, " "); | 345 return base::JoinString(sane_csp_parts, " "); |
338 } | 346 } |
339 | 347 |
348 std::string GetEffectiveSandoxedPageCSP(const std::string& policy) { | |
Devlin
2016/12/09 15:46:01
I kind of wonder if it makes sense to have a more
Charlie Reis
2016/12/09 19:55:07
I haven't seen one, but I agree that avoiding a bu
alexmos
2016/12/12 19:04:11
I haven't seen one either, and I agree it's a good
lazyboy
2016/12/14 00:49:05
I've introduced a CSPEnforcer class that can sanit
| |
349 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | |
350 std::vector<std::string> effective_csp_parts; | |
351 bool seen_default_source = false; | |
352 bool seen_subframe_source = false; | |
353 bool seen_script_source = false; | |
354 for (const std::string& directive : base::SplitString( | |
355 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | |
356 base::StringTokenizer tokenizer(directive, " \t\r\n"); | |
357 if (!tokenizer.GetNext()) | |
358 continue; | |
359 | |
360 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); | |
361 if (directive_name == kDefaultSrc) { | |
362 seen_default_source = true; | |
363 } else if (directive_name == kChildSrc || directive_name == kFrameSrc) { | |
364 seen_subframe_source = true; | |
365 } else if (directive_name == kScriptSrc) { | |
366 seen_script_source = true; | |
367 } else { | |
368 // Keep this directive as is. | |
369 effective_csp_parts.push_back(directive + kDirectiveSeparator); | |
370 continue; | |
371 } | |
372 | |
373 effective_csp_parts.push_back(directive_name); | |
374 bool seen_self_or_none = false; | |
375 while (tokenizer.GetNext()) { | |
376 std::string source_lower = base::ToLowerASCII(tokenizer.token()); | |
377 seen_self_or_none |= source_lower == "'none'" || source_lower == "'self'"; | |
378 | |
379 // Keyword directive sources are surrounded with quotes, e.g. 'self', | |
380 // 'sha256-...', 'unsafe-eval', 'nonce-...'. These do not specify a remote | |
381 // host or '*', so keep them and restrict the rest. | |
382 if (source_lower.size() > 1u && source_lower[0] == '\'' && | |
383 source_lower[source_lower.size() - 1] == '\'') { | |
Devlin
2016/12/09 15:46:01
When we see an insecure pattern, we could add an i
lazyboy
2016/12/14 00:49:05
Done.
| |
384 effective_csp_parts.push_back(source_lower); | |
385 } | |
386 } | |
387 | |
388 if (!seen_self_or_none) | |
389 effective_csp_parts.push_back("'self'"); | |
390 | |
391 // Add ";" at the end of the directive. | |
392 effective_csp_parts.back().append(kDirectiveSeparator); | |
393 } | |
394 | |
395 // If subframes, scripts were not restricted in |effective_csp_parts|, add the | |
396 // restriction. | |
397 if (!seen_subframe_source && !seen_default_source) | |
398 effective_csp_parts.push_back(kAppSandboxSubframeSrcDefaultDirective); | |
399 if (!seen_script_source && !seen_default_source) | |
400 effective_csp_parts.push_back(kAppSandboxScriptSrcDefaultDirective); | |
401 | |
402 return base::JoinString(effective_csp_parts, " "); | |
403 } | |
404 | |
340 bool ContentSecurityPolicyIsSandboxed( | 405 bool ContentSecurityPolicyIsSandboxed( |
341 const std::string& policy, Manifest::Type type) { | 406 const std::string& policy, Manifest::Type type) { |
342 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | 407 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. |
343 bool seen_sandbox = false; | 408 bool seen_sandbox = false; |
344 for (const std::string& input : base::SplitString( | 409 for (const std::string& input : base::SplitString( |
345 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | 410 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { |
346 base::StringTokenizer tokenizer(input, " \t\r\n"); | 411 base::StringTokenizer tokenizer(input, " \t\r\n"); |
347 if (!tokenizer.GetNext()) | 412 if (!tokenizer.GetNext()) |
348 continue; | 413 continue; |
349 | 414 |
(...skipping 17 matching lines...) Expand all Loading... | |
367 } | 432 } |
368 } | 433 } |
369 } | 434 } |
370 | 435 |
371 return seen_sandbox; | 436 return seen_sandbox; |
372 } | 437 } |
373 | 438 |
374 } // namespace csp_validator | 439 } // namespace csp_validator |
375 | 440 |
376 } // namespace extensions | 441 } // namespace extensions |
OLD | NEW |