⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 286853 in webkit


Ignore:
Timestamp:
Dec 10, 2021, 8:56:55 AM (5 years ago)
Author:
Patrick Griffis
Message:

CSP: Allow external scripts with SRI hashes matching CSP
https://bugs.webkit.org/show_bug.cgi?id=233911

Reviewed by Kate Cheney.

LayoutTests/imported/w3c:

Update expectations with more passes.

  • web-platform-tests/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy-expected.txt:
  • web-platform-tests/content-security-policy/script-src/script-src-sri_hash.sub-expected.txt:

Source/WebCore:

This is a change in CSP3 that allows loading external
scripts that have SRI hashes matching CSP hashes.
https://www.w3.org/TR/CSP3/#external-hash

ResourceCryptographicDigest was changed to not validate padding
during base64 decoding which is harmless and fixes parsing the
hashes used in WPT's script-src-sri_hash.sub.html.

  • loader/DocumentThreadableLoader.cpp:

(WebCore::DocumentThreadableLoader::isAllowedByContentSecurityPolicy):

  • loader/ResourceCryptographicDigest.cpp:

(WebCore::parseCryptographicDigestImpl):
(WebCore::decodeEncodedResourceCryptographicDigest):

  • loader/SubresourceIntegrity.cpp:

(WebCore::parseIntegrityMetadata):

  • loader/SubresourceIntegrity.h:
  • loader/cache/CachedResourceLoader.cpp:

(WebCore::CachedResourceLoader::allowedByContentSecurityPolicy const):

  • page/csp/ContentSecurityPolicy.cpp:

(WebCore::ContentSecurityPolicy::allScriptPoliciesAllow const):
(WebCore::parseSubResourceIntegrityIntoDigests):
(WebCore::ContentSecurityPolicy::allowResourceFromSource const):
(WebCore::ContentSecurityPolicy::allowScriptFromSource const):

  • page/csp/ContentSecurityPolicy.h:
  • page/csp/ContentSecurityPolicyDirectiveList.cpp:

(WebCore::ContentSecurityPolicyDirectiveList::violatedDirectiveForScript const):

  • page/csp/ContentSecurityPolicyDirectiveList.h:
  • page/csp/ContentSecurityPolicySourceList.cpp:

(WebCore::ContentSecurityPolicySourceList::matchesAll const):

  • page/csp/ContentSecurityPolicySourceList.h:
  • page/csp/ContentSecurityPolicySourceListDirective.cpp:

(WebCore::ContentSecurityPolicySourceListDirective::containsAllHashes const):

  • page/csp/ContentSecurityPolicySourceListDirective.h:

LayoutTests:

Update tests to accept base64 with extra padding.

  • http/tests/security/contentSecurityPolicy/1.1/scripthash-tests-expected.txt:
  • http/tests/security/contentSecurityPolicy/1.1/scripthash-tests.html:
Location:
trunk
Files:
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r286848 r286853  
     12021-12-10  Patrick Griffis  <pgriffis@igalia.com>
     2
     3        CSP: Allow external scripts with SRI hashes matching CSP
     4        https://bugs.webkit.org/show_bug.cgi?id=233911
     5
     6        Reviewed by Kate Cheney.
     7
     8        Update tests to accept base64 with extra padding.
     9
     10        * http/tests/security/contentSecurityPolicy/1.1/scripthash-tests-expected.txt:
     11        * http/tests/security/contentSecurityPolicy/1.1/scripthash-tests.html:
     12
    1132021-12-10  Alan Bujtas  <zalan@apple.com>
    214
  • trunk/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scripthash-tests-expected.txt

    r259900 r286853  
    1212CONSOLE MESSAGE: Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.
    1313CONSOLE MESSAGE: The source list for Content Security Policy directive 'script-src' contains an invalid source: ''sha256-/Vet2Rva6wwsny8xybL+=bQal0Gtf0FZW7EOVqqg+Hna=''. It will be ignored.
    14 CONSOLE MESSAGE: Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.
    15 CONSOLE MESSAGE: The source list for Content Security Policy directive 'script-src' contains an invalid source: ''sha256-/vET2rVA6WWSNY8XYBl+BqAL0gTF0fzw7eovQQG+hNA==''. It will be ignored.
    1614CONSOLE MESSAGE: Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.
    1715CONSOLE MESSAGE: The source list for Content Security Policy directive 'script-src' contains an invalid source: ''sha256-/vET2rVA6WWSNY8XYBl+BqAL0gTF0fzw7eovQQG+hNA===''. It will be ignored.
     
    5452PASS "SHA-256 hash with SHA-512 prefix" did not run inline script.
    5553PASS "Malformed SHA-256 hash (equal sign in disallowed position)" did not run inline script.
    56 PASS "SHA-256 hash with one extraneous equal sign" did not run inline script.
     54PASS "SHA-256 hash with one extraneous equal sign" did run inline script.
    5755PASS "SHA-256 hash with two extraneous equal signs" did not run inline script.
    5856PASS "Malformed hash source" did not run inline script.
  • trunk/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scripthash-tests.html

    r275917 r286853  
    160160    script: encodeURIComponent("didRunInlineScript = true;"),
    161161    hashSource: "'sha256-/vET2rVA6WWSNY8XYBl+BqAL0gTF0fzw7eovQQG+hNA=='",
    162     expectedResult: DoNotRunInlineScript,
     162    expectedResult: RunInlineScript,
    163163},
    164164{
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r286847 r286853  
     12021-12-10  Patrick Griffis  <pgriffis@igalia.com>
     2
     3        CSP: Allow external scripts with SRI hashes matching CSP
     4        https://bugs.webkit.org/show_bug.cgi?id=233911
     5
     6        Reviewed by Kate Cheney.
     7
     8        Update expectations with more passes.
     9
     10        * web-platform-tests/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy-expected.txt:
     11        * web-platform-tests/content-security-policy/script-src/script-src-sri_hash.sub-expected.txt:
     12
    1132021-12-10  Manuel Rego Casasnovas  <rego@igalia.com>
    214
  • trunk/LayoutTests/imported/w3c/web-platform-tests/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy-expected.txt

    r284254 r286853  
    11
    2 FAIL Should fire securitypolicyviolation event assert_equals: expected "report" but got "enforce"
    3 FAIL External script in a script tag with matching SRI hash should run. assert_true: External script ran. expected true got false
     2PASS Should fire securitypolicyviolation event
     3PASS External script in a script tag with matching SRI hash should run.
    44
  • trunk/LayoutTests/imported/w3c/web-platform-tests/content-security-policy/script-src/script-src-sri_hash.sub-expected.txt

    r267651 r286853  
    33
    44PASS Load all the tests.
    5 FAIL matching integrity assert_unreached: Script should load! http://localhost:8800/content-security-policy/script-src/simpleSourcedScript.js Reached unreachable code
    6 FAIL multiple matching integrity assert_unreached: Script should load! http://localhost:8800/content-security-policy/script-src/simpleSourcedScript.js Reached unreachable code
     5PASS matching integrity
     6PASS multiple matching integrity
    77PASS no integrity
    8 FAIL matching plus unsupported integrity assert_unreached: Script should load! http://localhost:8800/content-security-policy/script-src/simpleSourcedScript.js Reached unreachable code
     8PASS matching plus unsupported integrity
    99PASS mismatched integrity
    1010PASS multiple mismatched integrity
     
    1212FAIL crossorigin no integrity but allowed host assert_unreached: Script should load! http://127.0.0.1:8800/content-security-policy/script-src/crossoriginScript.js Reached unreachable code
    1313FAIL crossorigin mismatched integrity but allowed host assert_unreached: Script should load! http://127.0.0.1:8800/content-security-policy/script-src/crossoriginScript.js Reached unreachable code
    14 FAIL External script in a script tag with matching SRI hash should run. assert_true: External script ran. expected true got false
     14PASS External script in a script tag with matching SRI hash should run.
    1515
  • trunk/Source/WebCore/ChangeLog

    r286851 r286853  
     12021-12-10  Patrick Griffis  <pgriffis@igalia.com>
     2
     3        CSP: Allow external scripts with SRI hashes matching CSP
     4        https://bugs.webkit.org/show_bug.cgi?id=233911
     5
     6        Reviewed by Kate Cheney.
     7
     8        This is a change in CSP3 that allows loading external
     9        scripts that have SRI hashes matching CSP hashes.
     10        https://www.w3.org/TR/CSP3/#external-hash
     11
     12        ResourceCryptographicDigest was changed to not validate padding
     13        during base64 decoding which is harmless and fixes parsing the
     14        hashes used in WPT's script-src-sri_hash.sub.html.
     15
     16        * loader/DocumentThreadableLoader.cpp:
     17        (WebCore::DocumentThreadableLoader::isAllowedByContentSecurityPolicy):
     18        * loader/ResourceCryptographicDigest.cpp:
     19        (WebCore::parseCryptographicDigestImpl):
     20        (WebCore::decodeEncodedResourceCryptographicDigest):
     21        * loader/SubresourceIntegrity.cpp:
     22        (WebCore::parseIntegrityMetadata):
     23        * loader/SubresourceIntegrity.h:
     24        * loader/cache/CachedResourceLoader.cpp:
     25        (WebCore::CachedResourceLoader::allowedByContentSecurityPolicy const):
     26        * page/csp/ContentSecurityPolicy.cpp:
     27        (WebCore::ContentSecurityPolicy::allScriptPoliciesAllow const):
     28        (WebCore::parseSubResourceIntegrityIntoDigests):
     29        (WebCore::ContentSecurityPolicy::allowResourceFromSource const):
     30        (WebCore::ContentSecurityPolicy::allowScriptFromSource const):
     31        * page/csp/ContentSecurityPolicy.h:
     32        * page/csp/ContentSecurityPolicyDirectiveList.cpp:
     33        (WebCore::ContentSecurityPolicyDirectiveList::violatedDirectiveForScript const):
     34        * page/csp/ContentSecurityPolicyDirectiveList.h:
     35        * page/csp/ContentSecurityPolicySourceList.cpp:
     36        (WebCore::ContentSecurityPolicySourceList::matchesAll const):
     37        * page/csp/ContentSecurityPolicySourceList.h:
     38        * page/csp/ContentSecurityPolicySourceListDirective.cpp:
     39        (WebCore::ContentSecurityPolicySourceListDirective::containsAllHashes const):
     40        * page/csp/ContentSecurityPolicySourceListDirective.h:
     41
    1422021-12-10  Alan Bujtas  <zalan@apple.com>
    243
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp

    r286094 r286853  
    686686        return contentSecurityPolicy().allowConnectToSource(url, redirectResponseReceived, preRedirectURL);
    687687    case ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective:
    688         return contentSecurityPolicy().allowScriptFromSource(url, redirectResponseReceived, preRedirectURL);
     688        return contentSecurityPolicy().allowScriptFromSource(url, redirectResponseReceived, preRedirectURL, m_options.integrity);
    689689    }
    690690    ASSERT_NOT_REACHED();
  • trunk/Source/WebCore/loader/ResourceCryptographicDigest.cpp

    r278758 r286853  
    7272    StringView hashValue(beginHashValue, buffer.position() - beginHashValue);
    7373
    74     if (auto digest = base64Decode(hashValue, Base64DecodeOptions::ValidatePadding))
     74    if (auto digest = base64Decode(hashValue))
    7575        return ResourceCryptographicDigest { *algorithm, WTFMove(*digest) };
    7676
     
    126126std::optional<ResourceCryptographicDigest> decodeEncodedResourceCryptographicDigest(const EncodedResourceCryptographicDigest& encodedDigest)
    127127{
    128     if (auto digest = base64Decode(encodedDigest.digest, Base64DecodeOptions::ValidatePadding))
     128    if (auto digest = base64Decode(encodedDigest.digest))
    129129        return ResourceCryptographicDigest { encodedDigest.algorithm, WTFMove(*digest) };
    130130
  • trunk/Source/WebCore/loader/SubresourceIntegrity.cpp

    r278702 r286853  
    9797}
    9898
    99 static std::optional<Vector<EncodedResourceCryptographicDigest>> parseIntegrityMetadata(const String& integrityMetadata)
     99std::optional<Vector<EncodedResourceCryptographicDigest>> parseIntegrityMetadata(const String& integrityMetadata)
    100100{
    101101    if (integrityMetadata.isEmpty())
  • trunk/Source/WebCore/loader/SubresourceIntegrity.h

    r251582 r286853  
    3434bool matchIntegrityMetadata(const CachedResource&, const String& integrityMetadata);
    3535String integrityMismatchDescription(const CachedResource&, const String& integrityMetadata);
     36std::optional<Vector<EncodedResourceCryptographicDigest>> parseIntegrityMetadata(const String& integrityMetadata);
    3637
    3738}
  • trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp

    r286094 r286853  
    502502#endif
    503503    case CachedResource::Type::Script:
    504         if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived, preRedirectURL))
     504        if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived, preRedirectURL, options.integrity))
    505505            return false;
    506506        break;
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicy.cpp

    r286772 r286853  
    5353#include "SecurityPolicyViolationEvent.h"
    5454#include "Settings.h"
     55#include "SubresourceIntegrity.h"
    5556#include <JavaScriptCore/ScriptCallStack.h>
    5657#include <JavaScriptCore/ScriptCallStackFactory.h>
     
    340341        auto violatedDirectiveForNonParserInsertedScript = policy.get()->violatedDirectiveForParserInsertedScript(parserInserted);
    341342        auto violatedDirectiveForScriptNonce = policy.get()->violatedDirectiveForScriptNonce(nonce);
    342         auto violatedDirectiveForScriptSrc = policy.get()->violatedDirectiveForScript(url, false);
     343        auto violatedDirectiveForScriptSrc = policy.get()->violatedDirectiveForScript(url, false, { });
    343344        auto [foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies] = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts);
    344345
     
    641642}
    642643
    643 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL) const
     644static Vector<ResourceCryptographicDigest> parseSubResourceIntegrityIntoDigests(const String& subResourceIntegrity)
     645{
     646    auto encodedDigests = parseIntegrityMetadata(subResourceIntegrity);
     647    Vector<ResourceCryptographicDigest> decodedDigests;
     648
     649    if (!encodedDigests.has_value())
     650        return { };
     651
     652    for (const auto& encodedDigest : encodedDigests.value()) {
     653        auto decodedDigest = decodeEncodedResourceCryptographicDigest(encodedDigest);
     654        if (decodedDigest.has_value())
     655            decodedDigests.append(decodedDigest.value());
     656    }
     657
     658    return decodedDigests;
     659}
     660
     661bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL, const String& subResourceIntegrity) const
    644662{
    645663    if (shouldPerformEarlyCSPCheck())
    646664        return true;
    647 
    648     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::scriptSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript, preRedirectURL);
     665    if (LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
     666        return true;
     667
     668    String sourceURL;
     669    const auto& blockedURL = !preRedirectURL.isNull() ? preRedirectURL : url;
     670    TextPosition sourcePosition(OrdinalNumber::beforeFirst(), OrdinalNumber());
     671    auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
     672        String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, blockedURL, "Refused to load");
     673        reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, blockedURL.string(), consoleMessage, sourceURL, StringView(), sourcePosition);
     674    };
     675
     676    auto subResourceIntegrityDigests = parseSubResourceIntegrityIntoDigests(subResourceIntegrity);
     677    return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript, url, redirectResponseReceived == RedirectResponseReceived::Yes, subResourceIntegrityDigests);
    649678}
    650679
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicy.h

    r286772 r286853  
    114114
    115115    enum class RedirectResponseReceived { No, Yes };
    116     WEBCORE_EXPORT bool allowScriptFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const;
     116    WEBCORE_EXPORT bool allowScriptFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL(), const String& = nullString()) const;
    117117    bool allowImageFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const;
    118118    bool allowStyleFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const;
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp

    r286150 r286853  
    388388}
    389389
    390 const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScript(const URL& url, bool didReceiveRedirectResponse) const
     390const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScript(const URL& url, bool didReceiveRedirectResponse, const Vector<ResourceCryptographicDigest>& subResourceIntegrityDigests) const
    391391{
    392392    auto* operativeDirective = this->operativeDirective(m_scriptSrc.get(), ContentSecurityPolicyDirectiveNames::scriptSrcElem);
     393
     394    if (operativeDirective->containsAllHashes(subResourceIntegrityDigests))
     395        return nullptr;
     396
    393397    if (checkSource(operativeDirective, url, didReceiveRedirectResponse))
    394398        return nullptr;
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h

    r286150 r286853  
    7676    const ContentSecurityPolicyDirective* violatedDirectiveForObjectSource(const URL&, bool didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone) const;
    7777    const ContentSecurityPolicyDirective* violatedDirectiveForPluginType(const String& type, const String& typeAttribute) const;
    78     const ContentSecurityPolicyDirective* violatedDirectiveForScript(const URL&, bool didReceiveRedirectResponse) const;
     78    const ContentSecurityPolicyDirective* violatedDirectiveForScript(const URL&, bool didReceiveRedirectResponse, const Vector<ResourceCryptographicDigest>&) const;
    7979    const ContentSecurityPolicyDirective* violatedDirectiveForStyle(const URL&, bool didReceiveRedirectResponse) const;
    8080
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp

    r286772 r286853  
    156156}
    157157
     158bool ContentSecurityPolicySourceList::matchesAll(const Vector<ContentSecurityPolicyHash>& hashes) const
     159{
     160    if (hashes.isEmpty())
     161        return false;
     162
     163    for (auto& hash : hashes) {
     164        if (!m_hashes.contains(hash))
     165            return false;
     166    }
     167
     168    return true;
     169}
     170
    158171bool ContentSecurityPolicySourceList::matches(const String& nonce) const
    159172{
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h

    r286150 r286853  
    4646    bool matches(const URL&, bool didReceiveRedirectResponse) const;
    4747    bool matches(const Vector<ContentSecurityPolicyHash>&) const;
     48    bool matchesAll(const Vector<ContentSecurityPolicyHash>&) const;
    4849    bool matches(const String& nonce) const;
    4950
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp

    r284959 r286853  
    5353}
    5454
     55bool ContentSecurityPolicySourceListDirective::containsAllHashes(const Vector<ContentSecurityPolicyHash>& hashes) const
     56{
     57    return m_sourceList.matchesAll(hashes);
     58}
     59
    5560bool ContentSecurityPolicySourceListDirective::allows(const Vector<ContentSecurityPolicyHash>& hashes) const
    5661{
  • trunk/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h

    r286150 r286853  
    4141    bool allows(const URL&, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone);
    4242    bool allows(const Vector<ContentSecurityPolicyHash>&) const;
     43    bool containsAllHashes(const Vector<ContentSecurityPolicyHash>&) const;
    4344    bool allowUnsafeHashes(const Vector<ContentSecurityPolicyHash>&) const;
    4445    bool allows(const String& nonce) const;
Note: See TracChangeset for help on using the changeset viewer.