diff options
author | rob <rob@robwu.nl> | 2015-08-10 16:36:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-10 23:37:18 +0000 |
commit | 8131e678f35f3984805c049fcc66cc2d3ecc2d2d (patch) | |
tree | d9d10ff327422ec50d4416f67f02ab01d95050e8 /extensions/common/csp_validator.cc | |
parent | caa7fbff19c6451b185d70edd0e05516c3e856f9 (diff) | |
download | chromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.zip chromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.tar.gz chromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.tar.bz2 |
Support hash-source CSP directive in extensions/apps
Support CSP hashes (http://www.w3.org/TR/2015/CR-CSP2-20150721/#script-src-hash-usage).
The validation is strict and follows the standard by the letter.
Blink also accepts sha1 and URL-encoded base64, but the extension's CSP
validator only accepts the syntax as specified by the CSP2 standard.
This allows the Blink implementation to become standard-compliant in the
future without breaking extensions.
Also, the CSP validator will now preserve the case of the CSP tokens,
mainly because base64 is case-sensitive. And base::StringToLowerASCII
is deprecated, which is another reason to change to base::ToLowerASCII.
BUG=446036
Review URL: https://codereview.chromium.org/1285523002
Cr-Commit-Position: refs/heads/master@{#342737}
Diffstat (limited to 'extensions/common/csp_validator.cc')
-rw-r--r-- | extensions/common/csp_validator.cc | 68 |
1 files changed, 51 insertions, 17 deletions
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc index d19b983..842fc80 100644 --- a/extensions/common/csp_validator.cc +++ b/extensions/common/csp_validator.cc @@ -43,6 +43,15 @@ const char* const kSandboxedPluginTypes[] = { "application/x-pnacl" }; +// List of CSP hash-source prefixes that are accepted. Blink is a bit more +// lenient, but we only accept standard hashes to be forward-compatible. +// http://www.w3.org/TR/2015/CR-CSP2-20150721/#hash_algo +const char* const kHashSourcePrefixes[] = { + "'sha256-", + "'sha384-", + "'sha512-" +}; + struct DirectiveStatus { explicit DirectiveStatus(const char* name) : directive_name(name), seen_in_policy(false) {} @@ -113,6 +122,30 @@ bool isNonWildcardTLD(const std::string& url, return registry_length != 0; } +// Checks whether the source is a syntactically valid hash. +bool IsHashSource(const std::string& source) { + size_t hash_end = source.length() - 1; + if (source.empty() || source[hash_end] != '\'') { + return false; + } + + for (const char* prefix : kHashSourcePrefixes) { + if (base::StartsWith(source, prefix, + base::CompareCase::INSENSITIVE_ASCII)) { + for (size_t i = strlen(prefix); i < hash_end; ++i) { + const char c = source[i]; + // The hash must be base64-encoded. Do not allow any other characters. + if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) && c != '+' && + c != '/' && c != '=') { + return false; + } + } + return true; + } + } + return false; +} + InstallWarning CSPInstallWarning(const std::string& csp_warning) { return InstallWarning(csp_warning, manifest_keys::kContentSecurityPolicy); } @@ -124,38 +157,39 @@ void GetSecureDirectiveValues(const std::string& directive_name, std::vector<InstallWarning>* warnings) { sane_csp_parts->push_back(directive_name); while (tokenizer->GetNext()) { - std::string source = tokenizer->token(); - base::StringToLowerASCII(&source); + std::string source_literal = tokenizer->token(); + std::string source_lower = base::ToLowerASCII(source_literal); bool is_secure_csp_token = false; // We might need to relax this whitelist over time. - if (source == "'self'" || source == "'none'" || - source == "http://127.0.0.1" || - base::LowerCaseEqualsASCII(source, "blob:") || - base::LowerCaseEqualsASCII(source, "filesystem:") || - base::LowerCaseEqualsASCII(source, "http://localhost") || - base::StartsWith(source, "http://127.0.0.1:", + if (source_lower == "'self'" || source_lower == "'none'" || + source_lower == "http://127.0.0.1" || source_lower == "blob:" || + source_lower == "filesystem:" || source_lower == "http://localhost" || + base::StartsWith(source_lower, "http://127.0.0.1:", base::CompareCase::SENSITIVE) || - base::StartsWith(source, "http://localhost:", + base::StartsWith(source_lower, "http://localhost:", base::CompareCase::SENSITIVE) || - isNonWildcardTLD(source, "https://", true) || - isNonWildcardTLD(source, "chrome://", false) || - isNonWildcardTLD(source, std::string(extensions::kExtensionScheme) + - url::kStandardSchemeSeparator, + isNonWildcardTLD(source_lower, "https://", true) || + isNonWildcardTLD(source_lower, "chrome://", false) || + isNonWildcardTLD(source_lower, + std::string(extensions::kExtensionScheme) + + url::kStandardSchemeSeparator, false) || - base::StartsWith(source, "chrome-extension-resource:", + IsHashSource(source_literal) || + base::StartsWith(source_lower, "chrome-extension-resource:", base::CompareCase::SENSITIVE)) { is_secure_csp_token = true; } else if ((options & OPTIONS_ALLOW_UNSAFE_EVAL) && - source == "'unsafe-eval'") { + source_lower == "'unsafe-eval'") { is_secure_csp_token = true; } if (is_secure_csp_token) { - sane_csp_parts->push_back(source); + sane_csp_parts->push_back(source_literal); } else if (warnings) { warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage( - manifest_errors::kInvalidCSPInsecureValue, source, directive_name))); + manifest_errors::kInvalidCSPInsecureValue, source_literal, + directive_name))); } } // End of CSP directive that was started at the beginning of this method. If |