CSP: Implement support for script and style nonces
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 18:30:38 +0000 (18:30 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 18:30:38 +0000 (18:30 +0000)
https://bugs.webkit.org/show_bug.cgi?id=116508
<rdar://problem/24963980>

Reviewed by Brent Fulgham.

Source/WebCore:

Add support for script-src and style-src nonces as per sections Nonce usage for script elements
and Nonce usage for style elements of the Content Security Policy 2.0 spec., <https://www.w3.org/TR/2015/CR-CSP2-20150721/>.

* dom/InlineStyleSheetOwner.cpp:
(WebCore::InlineStyleSheetOwner::createSheet): Check if the nonce for an HTML style element matches a known nonce.
* dom/ScriptElement.cpp:
(WebCore::ScriptElement::requestScript): Check if the nonce for an HTML script element for an external JavaScript
script matches a known nonce. If it does then skip subsequent checks of the Content Security Policy when loading
the script.
(WebCore::ScriptElement::executeScript): Check if the nonce for an HTML script element for an inline JavaScript
script matches a known nonce.
* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::process): Check if the nonce for an HTML link element matches a known nonce. If it does
then skip subsequent checks of the Content Security Policy when loading the stylesheet.
* html/HTMLScriptElement.idl: Unconditionally expose attribute nonce.
* html/HTMLStyleElement.idl: Ditto.
* page/csp/ContentSecurityPolicy.cpp:
(WebCore::isAllowedByAllWithNonce):
(WebCore::ContentSecurityPolicy::allowScriptWithNonce): Check if the nonce attribute value of a script element
matches a known nonce. This function delegates the check to ContentSecurityPolicyDirectiveList::allowScriptWithNonce().
(WebCore::ContentSecurityPolicy::allowStyleWithNonce): Check if the nonce attribute value of a style/link element
matches a known nonce. This function delegates the check to ContentSecurityPolicyDirectiveList::allowStyleWithNonce().
* page/csp/ContentSecurityPolicy.h:
* page/csp/ContentSecurityPolicyDirectiveList.cpp:
(WebCore::checkNonce): Checks if the directive allows the specified nonce.
(WebCore::ContentSecurityPolicyDirectiveList::allowScriptWithNonce): Check if the specified nonce is in
the source list of the script-src directive (if specified) or the source list of the default-src directive (if specified).
(WebCore::ContentSecurityPolicyDirectiveList::allowStyleWithNonce): Check if the specified nonce is in
the source list of the style-src directive (if specified) or the source list of the default-src directive (if specified).
* page/csp/ContentSecurityPolicyDirectiveList.h:
* page/csp/ContentSecurityPolicySourceList.cpp:
(WebCore::ContentSecurityPolicySourceList::matches): Returns whether the specified nonce is in the HashSet of
nonces for the directive.
(WebCore::ContentSecurityPolicySourceList::parse): Modified to call ContentSecurityPolicySourceList::parseNonceSource()
to parse a nonce source expression.
(WebCore::isBase64Character): Moved function to be above function ContentSecurityPolicySourceList::parseNonceSource()
so that it can referenced from both ContentSecurityPolicySourceList::parseNonceSource() and ContentSecurityPolicySourceList::parseHashSource().
(WebCore::isNonceCharacter): Added. Matches Blink's definition of a valid nonce character. This definition differs
from the definition in the Content Security Policy Level 3 spec., <https://w3c.github.io/webappsec-csp/> (29 February 2016).
(WebCore::ContentSecurityPolicySourceList::parseNonceSource): Parses a source expression for a nonce value.
* page/csp/ContentSecurityPolicySourceList.h:
(WebCore::ContentSecurityPolicySourceList::allowInline): We only allow inline scripts/stylesheets if
'unsafe-inline' was specified in the source list and the source list does not contain any hash sources
or nonce sources.
* page/csp/ContentSecurityPolicySourceListDirective.cpp:
(WebCore::ContentSecurityPolicySourceListDirective::allows): Checks if the specified nonce is in the source list.
* page/csp/ContentSecurityPolicySourceListDirective.h:

LayoutTests:

Update expected results due to differences between Blink and WebKit violation message formats.

Additionally mark script- and style- nonce tests as PASS in LayoutTests/TestExpectations so
that we run them.

* TestExpectations:
* http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash-expected.txt:
* http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash.html:
* http/tests/security/contentSecurityPolicy/1.1/scriptnonce-basic-blocked-expected.txt:
* http/tests/security/contentSecurityPolicy/1.1/scriptnonce-ignore-unsafeinline-expected.txt:
* http/tests/security/contentSecurityPolicy/1.1/scriptnonce-invalidnonce-expected.txt: Also update result
to reflect that we consider "'n-" an invalid source expression (as expected).
* http/tests/security/contentSecurityPolicy/1.1/stylenonce-allowed-expected.txt:
* http/tests/security/contentSecurityPolicy/1.1/stylenonce-blocked-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197944 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash.html
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scriptnonce-basic-blocked-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scriptnonce-ignore-unsafeinline-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scriptnonce-invalidnonce-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/stylenonce-allowed-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/stylenonce-blocked-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/InlineStyleSheetOwner.cpp
Source/WebCore/dom/ScriptElement.cpp
Source/WebCore/html/HTMLLinkElement.cpp
Source/WebCore/html/HTMLScriptElement.idl
Source/WebCore/html/HTMLStyleElement.idl
Source/WebCore/page/csp/ContentSecurityPolicy.cpp
Source/WebCore/page/csp/ContentSecurityPolicy.h
Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp
Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h
Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp
Source/WebCore/page/csp/ContentSecurityPolicySourceList.h
Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp
Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h

index 22e74e2..96dfa31 100644 (file)
@@ -1,3 +1,26 @@
+2016-03-10  Daniel Bates  <dabates@apple.com>
+
+        CSP: Implement support for script and style nonces
+        https://bugs.webkit.org/show_bug.cgi?id=116508
+        <rdar://problem/24963980>
+
+        Reviewed by Brent Fulgham.
+
+        Update expected results due to differences between Blink and WebKit violation message formats.
+
+        Additionally mark script- and style- nonce tests as PASS in LayoutTests/TestExpectations so
+        that we run them.
+
+        * TestExpectations:
+        * http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash-expected.txt:
+        * http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash.html:
+        * http/tests/security/contentSecurityPolicy/1.1/scriptnonce-basic-blocked-expected.txt:
+        * http/tests/security/contentSecurityPolicy/1.1/scriptnonce-ignore-unsafeinline-expected.txt:
+        * http/tests/security/contentSecurityPolicy/1.1/scriptnonce-invalidnonce-expected.txt: Also update result
+        to reflect that we consider "'n-" an invalid source expression (as expected).
+        * http/tests/security/contentSecurityPolicy/1.1/stylenonce-allowed-expected.txt:
+        * http/tests/security/contentSecurityPolicy/1.1/stylenonce-blocked-expected.txt:
+
 2016-03-10  David Kilzer  <ddkilzer@apple.com>
 
         [iOS] DumpRenderTree crashes when accessing window.testRunner.inspectorTestStubURL
index 9650969..8fe7361 100644 (file)
@@ -826,6 +826,13 @@ http/tests/security/contentSecurityPolicy/1.1/scripthash-ignore-unsafeinline.htm
 http/tests/security/contentSecurityPolicy/1.1/scripthash-malformed.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/scripthash-tests.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/scripthash-unicode-normalization.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-allowed.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-and-scripthash.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-basic-blocked.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-blocked.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-ignore-unsafeinline.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-invalidnonce.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/scriptnonce-redirect.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/securitypolicyviolation-basics.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/securitypolicyviolation-block-cross-origin-image-from-script.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/securitypolicyviolation-block-cross-origin-image.html [ Pass ]
@@ -834,6 +841,8 @@ http/tests/security/contentSecurityPolicy/1.1/securitypolicyviolation-block-imag
 http/tests/security/contentSecurityPolicy/1.1/securitypolicyviolation-block-image.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/stylehash-allowed.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/stylehash-basic-blocked.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/stylenonce-allowed.html [ Pass ]
+http/tests/security/contentSecurityPolicy/1.1/stylenonce-blocked.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/plugintypes-affects-child.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html [ Pass ]
 http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html [ Pass ]
@@ -860,6 +869,8 @@ webkit.org/b/153155 http/tests/security/contentSecurityPolicy/style-src-blocked-
 webkit.org/b/153155 http/tests/security/contentSecurityPolicy/1.1/scripthash-basic-blocked-error-event.html
 webkit.org/b/153155 http/tests/security/contentSecurityPolicy/1.1/stylehash-basic-blocked-error-event.html
 webkit.org/b/153155 http/tests/security/contentSecurityPolicy/1.1/stylehash-svg-style-basic-blocked-error-event.html
+webkit.org/b/153155 http/tests/security/contentSecurityPolicy/1.1/stylenonce-basic-blocked-error-event.html
+webkit.org/b/153155 http/tests/security/contentSecurityPolicy/1.1/stylenonce-svg-style-basic-blocked-error-event.html
 webkit.org/b/153159 http/tests/security/contentSecurityPolicy/image-document-default-src-none.html [ Failure ]
 webkit.org/b/153160 http/tests/security/contentSecurityPolicy/object-src-does-not-affect-child.html [ Failure ]
 webkit.org/b/153160 http/tests/security/contentSecurityPolicy/plugin-in-iframe-with-csp.html [ Failure ]
index 5c7850d..168d3ed 100644 (file)
@@ -1,8 +1,8 @@
 ALERT: PASS (1/3)
 ALERT: PASS (2/3)
 ALERT: PASS (3/3)
-CONSOLE MESSAGE: line 16: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'sha1-MfuEFRkC2LmR31AMy9KW2ZLDegA=' 'sha1-p70t5PXyndLfjKNjbyBBOL1gFiM=' 'nonce-nonceynonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-F/kEIJZjYE56JaY4cy4cLGI8z6Rhynnwq6tihG4C9Ts='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 16: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'sha256-n8xN1Y8E6rPXuE8Iv++3Y3Smm6W4OGH6gqfZV7H8F1E=' 'nonce-nonceynonce'".
 
-CONSOLE MESSAGE: line 19: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'sha1-MfuEFRkC2LmR31AMy9KW2ZLDegA=' 'sha1-p70t5PXyndLfjKNjbyBBOL1gFiM=' 'nonce-nonceynonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-F26f0zRiJdDsn6iotp9pJMVvkqJpYZG+RG1eMpdaKZA='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 19: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'sha256-n8xN1Y8E6rPXuE8Iv++3Y3Smm6W4OGH6gqfZV7H8F1E=' 'nonce-nonceynonce'".
 
 This tests the combined use of script hash and script nonce. It passes if two console warnings are visible and the three alerts show PASS.
index 68e2e1a..c132e43 100644 (file)
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
     <head>
-        <meta http-equiv="Content-Security-Policy" content="script-src 'sha1-MfuEFRkC2LmR31AMy9KW2ZLDegA=' 'sha1-p70t5PXyndLfjKNjbyBBOL1gFiM=' 'nonce-nonceynonce'">
+        <meta http-equiv="Content-Security-Policy" content="script-src 'sha256-n8xN1Y8E6rPXuE8Iv++3Y3Smm6W4OGH6gqfZV7H8F1E=' 'nonce-nonceynonce'">
         <script nonce="nonceynonce">
             if (window.testRunner)
                 testRunner.dumpAsText();
index 88ef3ec..61fd091 100644 (file)
@@ -1,9 +1,9 @@
 ALERT: PASS (1/2)
 ALERT: PASS (2/2)
-CONSOLE MESSAGE: line 13: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-1RpMm8CnZ3w3FNN+LbHHalT1URf5MlDZCUee2MAF0/U='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 13: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'".
 
-CONSOLE MESSAGE: line 16: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-aA5X7j5nr6U308mrny9CeIP0WyoPjaNn/0piYZqVL7I='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 16: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'".
 
-CONSOLE MESSAGE: line 19: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-DnnF++Kbf/s2EyTBk12Rw2Ouq//L7GY5pcHSc6IZe6U='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 19: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce'".
 
 This tests the effect of a valid script-nonce value. It passes if three console warnings are visible, and the two PASS alerts are executed.
index df173e8..80b6535 100644 (file)
@@ -1,5 +1,5 @@
 ALERT: PASS (1/2)
 ALERT: PASS (2/2)
-CONSOLE MESSAGE: line 15: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce' 'nonce-noncy+/=nonce' 'unsafe-inline'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
+CONSOLE MESSAGE: line 15: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-noncynonce' 'nonce-noncy+/=nonce' 'unsafe-inline'".
 
 This tests that a valid nonce disables inline JavaScript, even if 'unsafe-inline' is present.
index efbe2f7..91f730c 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: The source list for Content Security Policy directive 'script-src' contains an invalid source: ''n'. It will be ignored.
 CONSOLE MESSAGE: Refused to load the script 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/script.js' because it violates the following Content Security Policy directive: "script-src 'n".
 
 CONSOLE MESSAGE: The source list for Content Security Policy directive 'script-src' contains an invalid source: ''nonce'. It will be ignored.
index 2ca8e09..4415f18 100644 (file)
@@ -1,6 +1,6 @@
-CONSOLE MESSAGE: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'nonce-noncynonce' 'nonce-noncy+/=nonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-SKwGvORdKBYTYiM4lxIkanDyKH8J0qJ5Ix8LGkKsbhw='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 1: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'nonce-noncynonce' 'nonce-noncy+/=nonce'".
 
-CONSOLE MESSAGE: line 11: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'nonce-noncynonce' 'nonce-noncy+/=nonce'". Either the 'unsafe-inline' keyword, a hash ('sha256-kv95ImKKneBhnSXrPlx5XNiVbPjFnuiudpQxG+M00io='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 11: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'nonce-noncynonce' 'nonce-noncy+/=nonce'".
 
 Style correctly whitelisted via a 'nonce-*' expression in 'style-src' should be applied to the page.
 
index b14d8fd..d2ad716 100644 (file)
@@ -1,6 +1,6 @@
-CONSOLE MESSAGE: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-SKwGvORdKBYTYiM4lxIkanDyKH8J0qJ5Ix8LGkKsbhw='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 1: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'".
 
-CONSOLE MESSAGE: line 6: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-cJwexfn/a5FXM2RqRmS0smWyEV/8Q3yAJM91YiT55c4='), or a nonce ('nonce-...') is required to enable inline execution.
+CONSOLE MESSAGE: line 6: Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'".
 
 Style that does not match a 'nonce-*' expression in 'style-src' should not be applied to the page.
 
index f7a63f8..59b43ca 100644 (file)
@@ -1,3 +1,59 @@
+2016-03-10  Daniel Bates  <dabates@apple.com>
+
+        CSP: Implement support for script and style nonces
+        https://bugs.webkit.org/show_bug.cgi?id=116508
+        <rdar://problem/24963980>
+
+        Reviewed by Brent Fulgham.
+
+        Add support for script-src and style-src nonces as per sections Nonce usage for script elements
+        and Nonce usage for style elements of the Content Security Policy 2.0 spec., <https://www.w3.org/TR/2015/CR-CSP2-20150721/>.
+
+        * dom/InlineStyleSheetOwner.cpp:
+        (WebCore::InlineStyleSheetOwner::createSheet): Check if the nonce for an HTML style element matches a known nonce.
+        * dom/ScriptElement.cpp:
+        (WebCore::ScriptElement::requestScript): Check if the nonce for an HTML script element for an external JavaScript
+        script matches a known nonce. If it does then skip subsequent checks of the Content Security Policy when loading
+        the script.
+        (WebCore::ScriptElement::executeScript): Check if the nonce for an HTML script element for an inline JavaScript
+        script matches a known nonce.
+        * html/HTMLLinkElement.cpp:
+        (WebCore::HTMLLinkElement::process): Check if the nonce for an HTML link element matches a known nonce. If it does
+        then skip subsequent checks of the Content Security Policy when loading the stylesheet.
+        * html/HTMLScriptElement.idl: Unconditionally expose attribute nonce.
+        * html/HTMLStyleElement.idl: Ditto.
+        * page/csp/ContentSecurityPolicy.cpp:
+        (WebCore::isAllowedByAllWithNonce):
+        (WebCore::ContentSecurityPolicy::allowScriptWithNonce): Check if the nonce attribute value of a script element
+        matches a known nonce. This function delegates the check to ContentSecurityPolicyDirectiveList::allowScriptWithNonce().
+        (WebCore::ContentSecurityPolicy::allowStyleWithNonce): Check if the nonce attribute value of a style/link element
+        matches a known nonce. This function delegates the check to ContentSecurityPolicyDirectiveList::allowStyleWithNonce().
+        * page/csp/ContentSecurityPolicy.h:
+        * page/csp/ContentSecurityPolicyDirectiveList.cpp:
+        (WebCore::checkNonce): Checks if the directive allows the specified nonce.
+        (WebCore::ContentSecurityPolicyDirectiveList::allowScriptWithNonce): Check if the specified nonce is in
+        the source list of the script-src directive (if specified) or the source list of the default-src directive (if specified).
+        (WebCore::ContentSecurityPolicyDirectiveList::allowStyleWithNonce): Check if the specified nonce is in
+        the source list of the style-src directive (if specified) or the source list of the default-src directive (if specified).
+        * page/csp/ContentSecurityPolicyDirectiveList.h:
+        * page/csp/ContentSecurityPolicySourceList.cpp:
+        (WebCore::ContentSecurityPolicySourceList::matches): Returns whether the specified nonce is in the HashSet of
+        nonces for the directive.
+        (WebCore::ContentSecurityPolicySourceList::parse): Modified to call ContentSecurityPolicySourceList::parseNonceSource()
+        to parse a nonce source expression.
+        (WebCore::isBase64Character): Moved function to be above function ContentSecurityPolicySourceList::parseNonceSource()
+        so that it can referenced from both ContentSecurityPolicySourceList::parseNonceSource() and ContentSecurityPolicySourceList::parseHashSource().
+        (WebCore::isNonceCharacter): Added. Matches Blink's definition of a valid nonce character. This definition differs
+        from the definition in the Content Security Policy Level 3 spec., <https://w3c.github.io/webappsec-csp/> (29 February 2016).
+        (WebCore::ContentSecurityPolicySourceList::parseNonceSource): Parses a source expression for a nonce value.
+        * page/csp/ContentSecurityPolicySourceList.h:
+        (WebCore::ContentSecurityPolicySourceList::allowInline): We only allow inline scripts/stylesheets if
+        'unsafe-inline' was specified in the source list and the source list does not contain any hash sources
+        or nonce sources.
+        * page/csp/ContentSecurityPolicySourceListDirective.cpp:
+        (WebCore::ContentSecurityPolicySourceListDirective::allows): Checks if the specified nonce is in the source list.
+        * page/csp/ContentSecurityPolicySourceListDirective.h:
+
 2016-03-08  Sam Weinig  <sam@webkit.org>
 
         Add a baseURL parameter to _WKUserStyleSheet
index 14faedb..6503415 100644 (file)
@@ -135,7 +135,11 @@ void InlineStyleSheetOwner::createSheet(Element& element, const String& text)
 
     if (!isValidCSSContentType(element, m_contentType))
         return;
-    if (!document.contentSecurityPolicy()->allowInlineStyle(document.url(), m_startTextPosition.m_line, text, element.isInUserAgentShadowTree()))
+
+    ASSERT(document.contentSecurityPolicy());
+    const ContentSecurityPolicy& contentSecurityPolicy = *document.contentSecurityPolicy();
+    bool hasKnownNonce = contentSecurityPolicy.allowStyleWithNonce(element.fastGetAttribute(HTMLNames::nonceAttr), element.isInUserAgentShadowTree());
+    if (!contentSecurityPolicy.allowInlineStyle(document.url(), m_startTextPosition.m_line, text, hasKnownNonce))
         return;
 
     RefPtr<MediaQuerySet> mediaQueries;
index 303cb07..f9c70e3 100644 (file)
@@ -258,8 +258,9 @@ bool ScriptElement::requestScript(const String& sourceUrl)
 
     ASSERT(!m_cachedScript);
     if (!stripLeadingAndTrailingHTMLSpaces(sourceUrl).isEmpty()) {
+        bool hasKnownNonce = m_element.document().contentSecurityPolicy()->allowScriptWithNonce(m_element.fastGetAttribute(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree());
         ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
-        options.setContentSecurityPolicyImposition(m_element.isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck);
+        options.setContentSecurityPolicyImposition(hasKnownNonce ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck);
 
         CachedResourceRequest request(ResourceRequest(m_element.document().completeURL(sourceUrl)), options);
 
@@ -293,8 +294,13 @@ void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
     if (sourceCode.isEmpty())
         return;
 
-    if (!m_isExternalScript && !m_element.document().contentSecurityPolicy()->allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), m_element.isInUserAgentShadowTree()))
-        return;
+    if (!m_isExternalScript) {
+        ASSERT(m_element.document().contentSecurityPolicy());
+        const ContentSecurityPolicy& contentSecurityPolicy = *m_element.document().contentSecurityPolicy();
+        bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(m_element.fastGetAttribute(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree());
+        if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce))
+            return;
+    }
 
 #if ENABLE(NOSNIFF)
     if (m_isExternalScript && m_cachedScript && !m_cachedScript->mimeTypeAllowedByNosniff()) {
index 49c15ce..4a73e53 100644 (file)
@@ -32,6 +32,7 @@
 #include "CachedResource.h"
 #include "CachedResourceLoader.h"
 #include "CachedResourceRequest.h"
+#include "ContentSecurityPolicy.h"
 #include "Document.h"
 #include "Event.h"
 #include "EventSender.h"
@@ -245,6 +246,13 @@ void HTMLLinkElement::process()
             priority = ResourceLoadPriority::VeryLow;
         CachedResourceRequest request(ResourceRequest(document().completeURL(url)), charset, priority);
         request.setInitiator(this);
+
+        if (document().contentSecurityPolicy()->allowStyleWithNonce(fastGetAttribute(HTMLNames::nonceAttr))) {
+            ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
+            options.setContentSecurityPolicyImposition(ContentSecurityPolicyImposition::SkipPolicyCheck);
+            request.setOptions(options);
+        }
+
         m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(request);
         
         if (m_cachedSheet)
index 7b6b32b..1998e4f 100644 (file)
@@ -29,5 +29,5 @@ interface HTMLScriptElement : HTMLElement {
     [Reflect, URL] attribute DOMString src;
     [Reflect] attribute DOMString type;
     attribute DOMString? crossOrigin;
-    [Reflect, Conditional=CSP_NEXT] attribute DOMString nonce;
+    [Reflect] attribute DOMString nonce;
 };
index 7abf383..fd3f7c0 100644 (file)
@@ -25,5 +25,7 @@ interface HTMLStyleElement : HTMLElement {
 
     // DOM Level 2 Style
     readonly attribute StyleSheet sheet;
+
+    [Reflect] attribute DOMString nonce;
 };
 
index c3fd0ef..e95d074 100644 (file)
@@ -39,6 +39,7 @@
 #include "FormData.h"
 #include "FormDataList.h"
 #include "Frame.h"
+#include "HTMLParserIdioms.h"
 #include "InspectorInstrumentation.h"
 #include "JSMainThreadExecState.h"
 #include "ParsingUtilities.h"
@@ -186,6 +187,16 @@ bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const Str
     return true;
 }
 
+template<bool (ContentSecurityPolicyDirectiveList::*allowed)(const String& nonce) const>
+static bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce)
+{
+    for (auto& policy : policies) {
+        if (!(policy.get()->*allowed)(nonce))
+            return false;
+    }
+    return true;
+}
+
 static CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm)
 {
     switch (algorithm) {
@@ -252,6 +263,30 @@ const TextEncoding& ContentSecurityPolicy::documentEncoding() const
     return UTF8Encoding();
 }
 
+bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
+{
+    if (overrideContentSecurityPolicy)
+        return true;
+    String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
+    if (strippedNonce.isEmpty())
+        return false;
+    if (isAllowedByAllWithNonce<&ContentSecurityPolicyDirectiveList::allowScriptWithNonce>(m_policies, strippedNonce))
+        return true;
+    return false;
+}
+
+bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
+{
+    if (overrideContentSecurityPolicy)
+        return true;
+    String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
+    if (strippedNonce.isEmpty())
+        return false;
+    if (isAllowedByAllWithNonce<&ContentSecurityPolicyDirectiveList::allowStyleWithNonce>(m_policies, strippedNonce))
+        return true;
+    return false;
+}
+
 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const
 {
     if (overrideContentSecurityPolicy)
index 4b959e1..a34d80f 100644 (file)
@@ -86,7 +86,9 @@ public:
     };
     bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
     bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
+    bool allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;
     bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
+    bool allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;
     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
     bool allowEval(JSC::ExecState* = nullptr, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
     bool allowPluginType(const String& type, const String& typeAttribute, const URL&, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const;
index 2ac09d8..47f5da4 100644 (file)
@@ -125,6 +125,11 @@ static inline bool checkHash(ContentSecurityPolicySourceListDirective* directive
     return !directive || directive->allows(hash);
 }
 
+static inline bool checkNonce(ContentSecurityPolicySourceListDirective* directive, const String& nonce)
+{
+    return !directive || directive->allows(nonce);
+}
+
 static inline bool checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute)
 {
     if (!directive)
@@ -288,6 +293,11 @@ bool ContentSecurityPolicyDirectiveList::allowInlineScriptWithHash(const Content
     return checkHash(operativeDirective(m_scriptSrc.get()), hash);
 }
 
+bool ContentSecurityPolicyDirectiveList::allowScriptWithNonce(const String& nonce) const
+{
+    return checkNonce(operativeDirective(m_scriptSrc.get()), nonce);
+}
+
 bool ContentSecurityPolicyDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
 {
     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: "));
@@ -301,6 +311,11 @@ bool ContentSecurityPolicyDirectiveList::allowInlineStyleWithHash(const ContentS
     return checkHash(operativeDirective(m_styleSrc.get()), hash);
 }
 
+bool ContentSecurityPolicyDirectiveList::allowStyleWithNonce(const String& nonce) const
+{
+    return checkNonce(operativeDirective(m_styleSrc.get()), nonce);
+}
+
 bool ContentSecurityPolicyDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const
 {
     static NeverDestroyed<String> consoleMessage(ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: "));
index 3f658fd..997c2a5 100644 (file)
@@ -52,8 +52,10 @@ public:
     bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
     bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
     bool allowInlineScriptWithHash(const ContentSecurityPolicyHash&) const;
+    bool allowScriptWithNonce(const String& nonce) const;
     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
     bool allowInlineStyleWithHash(const ContentSecurityPolicyHash&) const;
+    bool allowStyleWithNonce(const String& nonce) const;
     bool allowEval(JSC::ExecState*, ContentSecurityPolicy::ReportingStatus) const;
     bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ContentSecurityPolicy::ReportingStatus) const;
 
index fa79293..8044ae8 100644 (file)
@@ -132,6 +132,11 @@ bool ContentSecurityPolicySourceList::matches(const ContentSecurityPolicyHash& h
     return m_hashes.contains(hash);
 }
 
+bool ContentSecurityPolicySourceList::matches(const String& nonce) const
+{
+    return m_nonces.contains(nonce);
+}
+
 // source-list       = *WSP [ source *( 1*WSP source ) *WSP ]
 //                   / *WSP "'none'" *WSP
 //
@@ -152,6 +157,9 @@ void ContentSecurityPolicySourceList::parse(const UChar* begin, const UChar* end
         bool hostHasWildcard = false;
         bool portHasWildcard = false;
 
+        if (parseNonceSource(beginSource, position))
+            continue;
+
         if (parseHashSource(beginSource, position))
             continue;
 
@@ -395,6 +403,35 @@ bool ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar*
     return ok;
 }
 
+static bool isBase64Character(UChar c)
+{
+    return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
+}
+
+// Match Blink's behavior of allowing an equal sign to appear anywhere in the value of the nonce
+// even though this does not match the behavior of Content Security Policy Level 3 spec.,
+// <https://w3c.github.io/webappsec-csp/> (29 February 2016).
+static bool isNonceCharacter(UChar c)
+{
+    return isBase64Character(c) || c == '=';
+}
+
+// nonce-source    = "'nonce-" nonce-value "'"
+// nonce-value     = base64-value
+bool ContentSecurityPolicySourceList::parseNonceSource(const UChar* begin, const UChar* end)
+{
+    static NeverDestroyed<String> noncePrefix("'nonce-", String::ConstructFromLiteral);
+    if (!StringView(begin, end - begin).startsWithIgnoringASCIICase(noncePrefix.get()))
+        return false;
+    const UChar* position = begin + noncePrefix.get().length();
+    const UChar* beginNonceValue = position;
+    skipWhile<UChar, isNonceCharacter>(position, end);
+    if (position >= end || position == beginNonceValue || *position != '\'')
+        return false;
+    m_nonces.add(String(beginNonceValue, position - beginNonceValue));
+    return true;
+}
+
 static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t length, ContentSecurityPolicyHashAlgorithm& algorithm)
 {
     static struct {
@@ -418,11 +455,6 @@ static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t l
     return false;
 }
 
-static bool isBase64Character(UChar c)
-{
-    return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
-}
-
 // hash-source    = "'" hash-algorithm "-" base64-value "'"
 // hash-algorithm = "sha256" / "sha384" / "sha512"
 // base64-value  = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" )
index 0572dfc..811b6ce 100644 (file)
@@ -31,6 +31,7 @@
 #include "ContentSecurityPolicySource.h"
 #include <wtf/HashSet.h>
 #include <wtf/OptionSet.h>
+#include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -46,10 +47,11 @@ public:
 
     bool matches(const URL&);
     bool matches(const ContentSecurityPolicyHash&) const;
+    bool matches(const String& nonce) const;
 
     OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsUsed() const { return m_hashAlgorithmsUsed; }
 
-    bool allowInline() const { return m_allowInline && m_hashes.isEmpty(); }
+    bool allowInline() const { return m_allowInline && m_hashes.isEmpty() && m_nonces.isEmpty(); }
     bool allowEval() const { return m_allowEval; }
     bool allowSelf() const { return m_allowSelf; }
 
@@ -62,12 +64,15 @@ private:
     bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
     bool parsePath(const UChar* begin, const UChar* end, String& path);
 
+    bool parseNonceSource(const UChar* begin, const UChar* end);
+
     bool isProtocolAllowedByStar(const URL&) const;
 
     bool parseHashSource(const UChar* begin, const UChar* end);
 
     const ContentSecurityPolicy& m_policy;
     Vector<ContentSecurityPolicySource> m_list;
+    HashSet<String> m_nonces;
     HashSet<ContentSecurityPolicyHash> m_hashes;
     OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsUsed;
     String m_directiveName;
index a167f85..4c4f11c 100644 (file)
@@ -47,6 +47,11 @@ bool ContentSecurityPolicySourceListDirective::allows(const URL& url)
     return m_sourceList.matches(url);
 }
 
+bool ContentSecurityPolicySourceListDirective::allows(const String& nonce) const
+{
+    return m_sourceList.matches(nonce);
+}
+
 bool ContentSecurityPolicySourceListDirective::allows(const ContentSecurityPolicyHash& hash) const
 {
     return m_sourceList.matches(hash);
index c890903..ac65005 100644 (file)
@@ -40,6 +40,7 @@ public:
 
     bool allows(const URL&);
     bool allows(const ContentSecurityPolicyHash&) const;
+    bool allows(const String& nonce) const;
     bool allowInline() const { return m_sourceList.allowInline(); }
     bool allowEval() const { return m_sourceList.allowEval(); }