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

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: Minor fixes based on Adam's comments 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 650b43bf8e2cb3e6a41b9164f1bfe58838baf167..6d4479d5a02775f37fbc47edde0ad5ba36d47ef6 100644
--- a/Source/core/page/ContentSecurityPolicy.cpp
+++ b/Source/core/page/ContentSecurityPolicy.cpp
@@ -71,7 +71,7 @@ bool isDirectiveValueCharacter(UChar c)
bool isNonceCharacter(UChar c)
{
- return (c >= 0x21 && c <= 0x7e) && c != ',' && c != ';'; // VCHAR - ',' - ';'
+ return isASCIIAlphanumeric(c);
}
bool isSourceCharacter(UChar c)
@@ -126,7 +126,6 @@ static const char styleSrc[] = "style-src";
static const char baseURI[] = "base-uri";
static const char formAction[] = "form-action";
static const char pluginTypes[] = "plugin-types";
-static const char scriptNonce[] = "script-nonce";
static const char reflectedXSS[] = "reflected-xss";
bool isDirectiveName(const String& name)
@@ -145,7 +144,6 @@ bool isDirectiveName(const String& name)
|| equalIgnoringCase(name, baseURI)
|| equalIgnoringCase(name, formAction)
|| equalIgnoringCase(name, pluginTypes)
- || equalIgnoringCase(name, scriptNonce)
|| equalIgnoringCase(name, reflectedXSS)
);
}
@@ -313,6 +311,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);
@@ -322,11 +321,13 @@ private:
bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
bool parsePath(const UChar* begin, const UChar* end, String& path);
+ bool parseNonce(const UChar* begin, const UChar* end, String& nonce);
void addSourceSelf();
void addSourceStar();
void addSourceUnsafeInline();
void addSourceUnsafeEval();
+ void addSourceNonce(const String& nonce);
ContentSecurityPolicy* m_policy;
Vector<CSPSource> m_list;
@@ -334,6 +335,7 @@ private:
bool m_allowStar;
bool m_allowInline;
bool m_allowEval;
+ HashSet<String> m_nonces;
};
CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
@@ -438,6 +440,15 @@ bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
return true;
}
+ String nonce;
+ if (!parseNonce(begin, end, nonce))
+ return false;
+
+ if (!nonce.isNull()) {
+ addSourceNonce(nonce);
+ return true;
+ }
+
const UChar* position = begin;
const UChar* beginHost = begin;
const UChar* beginPath = end;
@@ -517,6 +528,29 @@ bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
return true;
}
+// nonce-source = "'nonce-" nonce-value "'"
+// nonce-value = *( ALPHA / DIGIT )
+//
+bool CSPSourceList::parseNonce(const UChar* begin, const UChar* end, String& nonce)
+{
+ DEFINE_STATIC_LOCAL(const String, noncePrefix, (ASCIILiteral("'nonce-")));
+
+ if (!equalIgnoringCase(noncePrefix.characters(), begin, noncePrefix.length()))
+ return true;
+
+ const UChar* position = begin + noncePrefix.length();
+ const UChar* nonceBegin = position;
+
+ skipWhile<isNonceCharacter>(position, end);
+ ASSERT(nonceBegin <= position);
+ nonce = String(nonceBegin, position - nonceBegin);
+
+ if ((position + 1) != end && *position != '\'')
+ return false;
+
+ return true;
+}
+
// ; <scheme> production from RFC 3986
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
//
@@ -654,6 +688,11 @@ void CSPSourceList::addSourceUnsafeEval()
m_allowEval = true;
}
+void CSPSourceList::addSourceNonce(const String& nonce)
+{
+ m_nonces.add(nonce);
+}
+
class CSPDirective {
public:
CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
@@ -674,50 +713,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)
@@ -810,6 +805,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;
@@ -828,7 +824,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;
@@ -841,6 +836,7 @@ public:
bool allowConnectToSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
bool allowFormAction(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
bool allowBaseURI(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
+ bool allowScriptNonce(const String&) const;
void gatherReportURIs(DOMStringList&) const;
const String& evalDisabledErrorMessage() { return m_evalDisabledErrorMessage; }
@@ -855,7 +851,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);
@@ -869,7 +864,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;
@@ -877,7 +872,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;
@@ -894,7 +888,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;
@@ -954,9 +947,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
@@ -995,14 +988,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))
@@ -1074,11 +1059,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()));
}
}
@@ -1086,11 +1069,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()));
}
}
@@ -1118,14 +1099,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 ?
@@ -1213,6 +1186,11 @@ bool CSPDirectiveList::allowBaseURI(const KURL& url, ContentSecurityPolicy::Repo
checkSource(m_baseURI.get(), url);
}
+bool CSPDirectiveList::allowScriptNonce(const String& nonce) const
+{
+ return checkNonce(operativeDirective(m_scriptSrc.get()), nonce);
+}
+
// policy = directive-list
// directive-list = [ directive *( ";" [ directive ] ) ]
//
@@ -1419,8 +1397,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
@@ -1526,16 +1502,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)
{
@@ -1585,11 +1560,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) {
@@ -1604,6 +1574,11 @@ bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecuri
return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
}
+bool ContentSecurityPolicy::allowScriptNonce(const String& nonce) const
+{
+ return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce);
+}
+
bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
{
return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
« Source/core/dom/ScriptElement.cpp ('K') | « Source/core/page/ContentSecurityPolicy.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698