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

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

Issue 2563843002: Restrict app sandbox's CSP to disallow loading web content in them. (Closed)
Patch Set: Created 4 years 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 <stddef.h> 7 #include <stddef.h>
8 8
9 #include <vector> 9 #include <vector>
10 10
(...skipping 10 matching lines...) Expand all
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698