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

Unified Diff: Source/core/page/ContentSecurityPolicy.cpp

Issue 14949017: Implementation of W3C compliant CSP script-src nonce. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: Source/core/page/ContentSecurityPolicy.cpp
diff --git a/Source/core/page/ContentSecurityPolicy.cpp b/Source/core/page/ContentSecurityPolicy.cpp
index 7aea7903bb3f7f7e2889577aa5ee2624d6ceeace..94bf785f9717098a6bb18f29a4d3faa3320168ea 100644
--- a/Source/core/page/ContentSecurityPolicy.cpp
+++ b/Source/core/page/ContentSecurityPolicy.cpp
@@ -69,7 +69,7 @@ bool isDirectiveValueCharacter(UChar c)
bool isNonceCharacter(UChar c)
{
- return (c >= 0x21 && c <= 0x7e) && c != ',' && c != ';'; // VCHAR - ',' - ';'
+ return isASCIIAlphanumeric(c);
}
bool isSourceCharacter(UChar c)
@@ -320,6 +320,7 @@ public:
bool matches(const KURL&);
bool allowInline() const { return m_allowInline; }
bool allowEval() const { return m_allowEval; }
+ bool allowNonce(const String& nonce) const { return !nonce.isNull() && m_nonces.contains(nonce); }
private:
void parse(const UChar* begin, const UChar* end);
@@ -334,6 +335,7 @@ private:
void addSourceStar();
void addSourceUnsafeInline();
void addSourceUnsafeEval();
+ void addSourceNonce(String& nonce);
abarth-chromium 2013/05/14 05:58:16 const String& <-- we generally use const referen
jww 2013/05/14 20:49:30 Done.
ContentSecurityPolicy* m_policy;
Vector<CSPSource> m_list;
@@ -341,6 +343,8 @@ private:
bool m_allowStar;
bool m_allowInline;
bool m_allowEval;
+ // Set of nonces that indicate whitelisted scripts
abarth-chromium 2013/05/14 05:58:16 I'd skip this comment. CSPSourceList isn't specif
jww 2013/05/14 20:49:30 Done.
+ HashSet<String> m_nonces;
};
CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
@@ -445,7 +449,41 @@ bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
return true;
}
- const UChar* position = begin;
+ const UChar* position;
abarth-chromium 2013/05/14 05:58:16 There's no reason to have this variable declared h
jww 2013/05/14 20:49:30 I declared it up here because I actually use it in
+
+ const char noncePrefix[] = "'nonce-";
+ const int noncePrefixLen = strlen(noncePrefix);
abarth-chromium 2013/05/14 05:58:16 It's better if you use a String that's an ASCIILit
jww 2013/05/14 20:49:30 I'm unclear on how to get the length in a compiler
+
+ if (equalIgnoringCase(noncePrefix, begin, noncePrefixLen) && (*(end - 1) == '\'')) {
abarth-chromium 2013/05/14 05:58:16 There's no reason to check the end - 1 condition.
Mike West 2013/05/14 08:07:38 Given the length, it might also be reasonable to s
jww 2013/05/14 20:49:30 Done.
jww 2013/05/14 20:49:30 Done.
+ String nonce;
+ const UChar* nonceBegin = position = begin + noncePrefixLen;
abarth-chromium 2013/05/14 05:58:16 We generally don't use compound statements like th
jww 2013/05/14 20:49:30 Done.
+ skipWhile<isNonceCharacter>(position, end);
+
+ // According to the W3C spec, we need to accept the empty string as a
+ // valid nonce (that is, "script-src 'nonce-'" is valid and is a nonce
+ // of the empty string ""). Hence why we do a <= comparrion rather than
+ // just a < comparrison.
+ if (nonceBegin <= position) {
abarth-chromium 2013/05/14 05:58:16 There's no way this can fail to be true. I'd just
jww 2013/05/14 20:49:30 Done.
+ nonce = String(nonceBegin, position - nonceBegin);
+ } else {
+ // This is a really odd error condition. Maybe this should be an
+ // ASSERT or do-not-reach?
+ return false;
+ }
+
+ if ((position + 1) != end && *position != '\'') {
+ // There is an invalid nonce character here. We should report the
+ // invalid character, but also eat up any characters until the
+ // single quote.
+ position = end;
abarth-chromium 2013/05/14 05:58:16 Position is a local variable. THere's no reason t
jww 2013/05/14 20:49:30 Whoops. Remnants of an old misunderstanding :-) On
+ return false;
+ }
+
+ addSourceNonce(nonce);
+ return true;
+ }
+
+ position = begin;
const UChar* beginHost = begin;
const UChar* beginPath = end;
const UChar* beginPort = 0;
@@ -661,6 +699,11 @@ void CSPSourceList::addSourceUnsafeEval()
m_allowEval = true;
}
+void CSPSourceList::addSourceNonce(String& nonce)
+{
+ m_nonces.add(String(nonce));
abarth-chromium 2013/05/14 05:58:16 There's no reason to call the String constructor a
jww 2013/05/14 20:49:30 Done.
+}
+
class CSPDirective {
public:
CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
@@ -681,50 +724,6 @@ private:
ContentSecurityPolicy* m_policy;
};
-class NonceDirective : public CSPDirective {
-public:
- NonceDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
- : CSPDirective(name, value, policy)
- {
- parse(value);
- }
-
- bool allows(const String& nonce) const
- {
- return (!m_scriptNonce.isEmpty() && nonce.stripWhiteSpace() == m_scriptNonce);
- }
-
-private:
- void parse(const String& value)
- {
- String nonce;
- const UChar* position = value.characters();
- const UChar* end = position + value.length();
-
- skipWhile<isASCIISpace>(position, end);
- const UChar* nonceBegin = position;
- if (position == end) {
- policy()->reportInvalidNonce(String());
- m_scriptNonce = "";
- return;
- }
- skipWhile<isNonceCharacter>(position, end);
- if (nonceBegin < position)
- nonce = String(nonceBegin, position - nonceBegin);
-
- // Trim off trailing whitespace: If we're not at the end of the string, log
- // an error.
- skipWhile<isASCIISpace>(position, end);
- if (position < end) {
- policy()->reportInvalidNonce(value);
- m_scriptNonce = "";
- } else
- m_scriptNonce = nonce;
- }
-
- String m_scriptNonce;
-};
-
class MediaListDirective : public CSPDirective {
public:
MediaListDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
@@ -817,6 +816,7 @@ public:
bool allowInline() const { return m_sourceList.allowInline(); }
bool allowEval() const { return m_sourceList.allowEval(); }
+ bool allowNonce(const String& nonce) const { return m_sourceList.allowNonce(nonce.stripWhiteSpace()); }
private:
CSPSourceList m_sourceList;
@@ -835,7 +835,6 @@ public:
bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
bool allowEval(ScriptState*, ContentSecurityPolicy::ReportingStatus) const;
- bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL&) const;
bool allowPluginType(const String& type, const String& typeAttribute, const KURL&, ContentSecurityPolicy::ReportingStatus) const;
bool allowScriptFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
@@ -848,6 +847,7 @@ public:
bool allowConnectToSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
bool allowFormAction(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
bool allowBaseURI(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
+ bool allowNonce(const String&) const;
void gatherReportURIs(DOMStringList&) const;
const String& evalDisabledErrorMessage() { return m_evalDisabledErrorMessage; }
@@ -862,7 +862,6 @@ private:
bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
void parseReportURI(const String& name, const String& value);
- void parseScriptNonce(const String& name, const String& value);
void parsePluginTypes(const String& name, const String& value);
void parseReflectedXSS(const String& name, const String& value);
void addDirective(const String& name, const String& value);
@@ -876,7 +875,7 @@ private:
bool checkEval(SourceListDirective*) const;
bool checkInline(SourceListDirective*) const;
- bool checkNonce(NonceDirective*, const String&) const;
+ bool checkNonce(SourceListDirective*, const String&) const;
bool checkSource(SourceListDirective*, const KURL&) const;
bool checkMediaType(MediaListDirective*, const String& type, const String& typeAttribute) const;
@@ -884,7 +883,6 @@ private:
bool checkEvalAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), ScriptState* = 0) const;
bool checkInlineAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const;
- bool checkNonceAndReportViolation(NonceDirective*, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
bool checkSourceAndReportViolation(SourceListDirective*, const KURL&, const String& effectiveDirective) const;
bool checkMediaTypeAndReportViolation(MediaListDirective*, const String& type, const String& typeAttribute, const String& consoleMessage) const;
@@ -901,7 +899,6 @@ private:
ContentSecurityPolicy::ReflectedXSSDisposition m_reflectedXSSDisposition;
OwnPtr<MediaListDirective> m_pluginTypes;
- OwnPtr<NonceDirective> m_scriptNonce;
OwnPtr<SourceListDirective> m_baseURI;
OwnPtr<SourceListDirective> m_connectSrc;
OwnPtr<SourceListDirective> m_defaultSrc;
@@ -961,9 +958,9 @@ bool CSPDirectiveList::checkInline(SourceListDirective* directive) const
return !directive || directive->allowInline();
}
-bool CSPDirectiveList::checkNonce(NonceDirective* directive, const String& nonce) const
+bool CSPDirectiveList::checkNonce(SourceListDirective* directive, const String& nonce) const
{
- return !directive || directive->allows(nonce);
+ return !directive || directive->allowNonce(nonce);
}
bool CSPDirectiveList::checkSource(SourceListDirective* directive, const KURL& url) const
@@ -1002,14 +999,6 @@ bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directiv
return true;
}
-bool CSPDirectiveList::checkNonceAndReportViolation(NonceDirective* directive, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
-{
- if (checkNonce(directive, nonce))
- return true;
- reportViolation(directive->text(), scriptNonce, consoleMessage + "\"" + directive->text() + "\".\n", KURL(), contextURL, contextLine);
- return denyIfEnforcingPolicy();
-}
-
bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const
{
if (checkMediaType(directive, type, typeAttribute))
@@ -1081,11 +1070,9 @@ bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::
{
DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: ")));
if (reportingStatus == ContentSecurityPolicy::SendReport) {
- return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
- && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine));
+ return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
} else {
- return (checkInline(operativeDirective(m_scriptSrc.get()))
- && checkNonce(m_scriptNonce.get(), String()));
+ return checkInline(operativeDirective(m_scriptSrc.get()));
}
}
@@ -1093,11 +1080,9 @@ bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const
{
DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute inline event handler because it violates the following Content Security Policy directive: ")));
if (reportingStatus == ContentSecurityPolicy::SendReport) {
- return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
- && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine));
+ return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true);
} else {
- return (checkInline(operativeDirective(m_scriptSrc.get()))
- && checkNonce(m_scriptNonce.get(), String()));
+ return checkInline(operativeDirective(m_scriptSrc.get()));
}
}
@@ -1125,14 +1110,6 @@ bool CSPDirectiveList::allowEval(ScriptState* state, ContentSecurityPolicy::Repo
checkEval(operativeDirective(m_scriptSrc.get()));
}
-bool CSPDirectiveList::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url) const
-{
- DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute script because it violates the following Content Security Policy directive: ")));
- if (url.isEmpty())
- return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, consoleMessage, contextURL, contextLine);
- return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, "Refused to load '" + url.elidedString() + "' because it violates the following Content Security Policy directive: ", contextURL, contextLine);
-}
-
bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
return reportingStatus == ContentSecurityPolicy::SendReport ?
@@ -1220,6 +1197,11 @@ bool CSPDirectiveList::allowBaseURI(const KURL& url, ContentSecurityPolicy::Repo
checkSource(m_baseURI.get(), url);
}
+bool CSPDirectiveList::allowNonce(const String& nonce) const
+{
+ return checkNonce(operativeDirective(m_scriptSrc.get()), nonce);
+}
+
// policy = directive-list
// directive-list = [ directive *( ";" [ directive ] ) ]
//
@@ -1426,8 +1408,6 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
setCSPDirective<SourceListDirective>(name, value, m_formAction);
else if (equalIgnoringCase(name, pluginTypes))
setCSPDirective<MediaListDirective>(name, value, m_pluginTypes);
- else if (equalIgnoringCase(name, scriptNonce))
- setCSPDirective<NonceDirective>(name, value, m_scriptNonce);
else if (equalIgnoringCase(name, reflectedXSS))
parseReflectedXSS(name, value);
else
@@ -1533,16 +1513,15 @@ bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const Str
return true;
}
-template<bool (CSPDirectiveList::*allowed)(const String&, const String&, const WTF::OrdinalNumber&, const KURL&) const>
-bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url)
+template<bool (CSPDirectiveList::*allowed)(const String&) const>
+bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce)
{
for (size_t i = 0; i < policies.size(); ++i) {
- if (!(policies[i].get()->*allowed)(nonce, contextURL, contextLine, url))
+ if (!(policies[i].get()->*allowed)(nonce))
return false;
}
return true;
}
-
template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const>
bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
{
@@ -1566,9 +1545,10 @@ bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, c
return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
}
-bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+bool ContentSecurityPolicy::allowInlineScript(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
- return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
+ bool nonceAllowed = isAllowedByAllWithNonce<&CSPDirectiveList::allowNonce>(m_policies, nonce);
+ return nonceAllowed || isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
}
bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
@@ -1592,11 +1572,6 @@ String ContentSecurityPolicy::evalDisabledErrorMessage() const
return String();
}
-bool ContentSecurityPolicy::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url) const
-{
- return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce, contextURL, contextLine, url);
-}
-
bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
for (size_t i = 0; i < m_policies.size(); ++i) {
@@ -1606,9 +1581,10 @@ bool ContentSecurityPolicy::allowPluginType(const String& type, const String& ty
return true;
}
-bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, const String& nonce, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
- return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
+ bool nonceAllowed = isAllowedByAllWithNonce<&CSPDirectiveList::allowNonce>(m_policies, nonce);
+ return nonceAllowed || isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
}
bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const

Powered by Google App Engine
This is Rietveld 408576698