Changeset 230602 in webkit


Ignore:
Timestamp:
Apr 12, 2018, 3:32:40 PM (7 years ago)
Author:
dbates@webkit.org
Message:

Content-Type not enforced for <script> allows for XSS
https://bugs.webkit.org/show_bug.cgi?id=184386
<rdar://problem/39112268>

Reviewed by Brady Eidson.

LayoutTests/imported/w3c:

Update expected result now that we pass all sub tests.

  • web-platform-tests/fetch/api/basic/block-mime-as-script-expected.txt:

Source/WebCore:

As per the Fetch standard, <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?> (16 March 2018),
we should block JavaScript scripts that are served with MIME type text/csv, or a MIME type
that begins with "audio/", "image/" or "video/".

As a side benefit of this change we now set the destination property [1] on preload requests.

[1] <https://fetch.spec.whatwg.org/#concept-request-destination>

Tests: http/tests/security/script-with-banned-mimetype.html

http/tests/workers/worker-importScripts-banned-mimetype.html

  • Sources.txt: Add file FetchIdioms.cpp.
  • WebCore.xcodeproj/project.pbxproj: Add files FetchIdioms.{cpp, h}.
  • dom/LoadableClassicScript.cpp:

(WebCore::LoadableClassicScript::notifyFinished): Check the MIME type of the response and
block the script if applicable.

  • dom/LoadableScript.h: Add error type MIMEType.
  • loader/FetchIdioms.cpp: Added.

(WebCore::shouldBlockResponseDueToMIMEType): Implements the "Should response to request be blocked
due to its MIME type?" algorithm from the Fetch standard.

  • loader/FetchIdioms.h: Added.
  • loader/FetchOptions.h:

(WebCore::isScriptLikeDestination): Implements the definition of "script like" as per <https://fetch.spec.whatwg.org/#request-destination-script-like>.

  • loader/cache/CachedResourceLoader.cpp:

(WebCore::CachedResourceLoader::requestImage): Removed logic to set the destination property as
CachedResourceLoader::requestResource() is now responsible for doing this.
(WebCore::CachedResourceLoader::requestFont): Ditto.
(WebCore::CachedResourceLoader::requestTextTrack): Ditto.
(WebCore::CachedResourceLoader::requestCSSStyleSheet): Ditto.
(WebCore::CachedResourceLoader::requestScript): Ditto.
(WebCore::CachedResourceLoader::requestXSLStyleSheet): Ditto.
(WebCore::CachedResourceLoader::requestMedia): Update comment to express that we should assert
that the destination property is either video or audio.
(WebCore::CachedResourceLoader::requestIcon): Remove logic to set the destination property as
CachedResourceLoader::requestResource() is now responsible for doing this.
(WebCore::CachedResourceLoader::requestRawResource): Removed assertion as this function is used to
load many kinds of requests that have different destination properties. The caller is responsible
for setting the appropriate destintion property.
(WebCore::CachedResourceLoader::requestMainResource): Remove logic to set the destination property
as CachedResourceLoader::requestResource() is now responsible for doing this.
(WebCore::destinationForType): Helper function that maps CachedResource::Type to FetchOptions::Destination.
(WebCore::CachedResourceLoader::requestResource): Set the destination property on the request if not
already set.

  • loader/cache/CachedResourceLoader.h: Segregate requestRawResource() from the other request functions

and add a comment to explain what it is used for.

  • workers/Worker.cpp:

(WebCore::Worker::create):

  • workers/WorkerScriptLoader.cpp:

(WebCore::WorkerScriptLoader::loadSynchronously): Set the destination property to FetchOptions::Destination::Script
and store it in an instance variable as we will need to reference it once we receive the HTTP response.
Note that this function is only used to support the Web API importScripts().
(WebCore::WorkerScriptLoader::loadAsynchronously): Store the passed destination property in an
instance as we will need to reference it once we receive the HTTP response.
(WebCore::WorkerScriptLoader::didReceiveResponse): Check the MIME type of the response and
block the script if applicable.

  • workers/WorkerScriptLoader.h:
  • workers/service/ServiceWorkerJob.cpp:

(WebCore::ServiceWorkerJob::fetchScriptWithContext): Set the destination property to FetchOptions::Destination::Serviceworker.

LayoutTests:

Add tests to ensure that we block JavaScript scripts with a banned MIME type and update expected results.

Update tests http/tests/security/{cross-origin-cached-scripts, cross-origin-cached-scripts-parallel}.html
to load JavaScript scripts with MIME type text/javascript. These tests load JavaScript scripts indirectly
via the helper script LayoutTests/http/tests/security/resources/allow-if-origin.php. The script
allow-if-origin.php returns a response with MIME type image/png in absence of query string argument
contentType. We need to update these tests to pass contentType=text/javascript to allow-if-origin.php.

  • TestExpectations: Mark test web-platform-tests/fetch/api/basic/block-mime-as-script.html DumpJSConsoleLogInStdErr

to ignore console message output when comparing the actual and expected result because the order the
sub tests are run is non-deterministic and the blocked MIME error message is specific to the blocked
response.

  • http/tests/security/contentTypeOptions/invalid-content-type-options-allowed-expected.txt:
  • http/tests/security/contentTypeOptions/invalid-content-type-options-allowed.html:
  • http/tests/security/contentTypeOptions/resources/script-with-header.pl: Modified to only

set the HTTP header X-Content-Type-Options if the query argument no-content-type-options it
not present or evaluates to false in a boolean context. This lets us make use of this script
to test banned JavaScript MIME types.

  • http/tests/security/cross-origin-cached-scripts-expected.txt:
  • http/tests/security/cross-origin-cached-scripts-parallel-expected.txt:
  • http/tests/security/cross-origin-cached-scripts-parallel.html:
  • http/tests/security/cross-origin-cached-scripts.html:
  • http/tests/security/resources/abe-that-increments-scriptsSuccessfullyLoaded.jpg: Added.

This file is both a valid JPEG image and a valid JavaScript script. When interpreted as a JavaScript
script it will increment the global variable self.scriptsSuccessfullyLoaded (defining it if
not already defined).

  • http/tests/security/script-with-banned-mimetype-expected.txt: Added.
  • http/tests/security/script-with-banned-mimetype.html: Added.
  • http/tests/workers/resources/worker-importScripts-banned-mimetype.php: Added.
  • http/tests/workers/worker-importScripts-banned-mimetype-expected.txt: Added.
  • http/tests/workers/worker-importScripts-banned-mimetype.html: Added.
Location:
trunk
Files:
8 added
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r230595 r230602  
     12018-04-12  Daniel Bates  <dabates@apple.com>
     2
     3        Content-Type not enforced for <script> allows for XSS
     4        https://bugs.webkit.org/show_bug.cgi?id=184386
     5        <rdar://problem/39112268>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Add tests to ensure that we block JavaScript scripts with a banned MIME type and update expected results.
     10
     11        Update tests http/tests/security/{cross-origin-cached-scripts, cross-origin-cached-scripts-parallel}.html
     12        to load JavaScript scripts with MIME type text/javascript. These tests load JavaScript scripts indirectly
     13        via the helper script LayoutTests/http/tests/security/resources/allow-if-origin.php. The script
     14        allow-if-origin.php returns a response with MIME type image/png in absence of query string argument
     15        contentType. We need to update these tests to pass contentType=text/javascript to allow-if-origin.php.
     16
     17        * TestExpectations: Mark test web-platform-tests/fetch/api/basic/block-mime-as-script.html DumpJSConsoleLogInStdErr
     18        to ignore console message output when comparing the actual and expected result because the order the
     19        sub tests are run is non-deterministic and the blocked MIME error message is specific to the blocked
     20        response.
     21        * http/tests/security/contentTypeOptions/invalid-content-type-options-allowed-expected.txt:
     22        * http/tests/security/contentTypeOptions/invalid-content-type-options-allowed.html:
     23        * http/tests/security/contentTypeOptions/resources/script-with-header.pl: Modified to only
     24        set the HTTP header X-Content-Type-Options if the query argument no-content-type-options it
     25        not present or evaluates to false in a boolean context. This lets us make use of this script
     26        to test banned JavaScript MIME types.
     27        * http/tests/security/cross-origin-cached-scripts-expected.txt:
     28        * http/tests/security/cross-origin-cached-scripts-parallel-expected.txt:
     29        * http/tests/security/cross-origin-cached-scripts-parallel.html:
     30        * http/tests/security/cross-origin-cached-scripts.html:
     31        * http/tests/security/resources/abe-that-increments-scriptsSuccessfullyLoaded.jpg: Added.
     32        This file is both a valid JPEG image and a valid JavaScript script. When interpreted as a JavaScript
     33        script it will increment the global variable self.scriptsSuccessfullyLoaded (defining it if
     34        not already defined).
     35        * http/tests/security/script-with-banned-mimetype-expected.txt: Added.
     36        * http/tests/security/script-with-banned-mimetype.html: Added.
     37        * http/tests/workers/resources/worker-importScripts-banned-mimetype.php: Added.
     38        * http/tests/workers/worker-importScripts-banned-mimetype-expected.txt: Added.
     39        * http/tests/workers/worker-importScripts-banned-mimetype.html: Added.
     40
    1412018-04-12  Antoine Quint  <graouts@apple.com>
    242
  • trunk/LayoutTests/TestExpectations

    r230590 r230602  
    609609imported/w3c/web-platform-tests/XMLHttpRequest/responsexml-document-properties.htm [ Failure ]
    610610
     611imported/w3c/web-platform-tests/fetch/api/basic/block-mime-as-script.html [ DumpJSConsoleLogInStdErr ]
    611612imported/w3c/web-platform-tests/fetch/api/cors/cors-origin.any.html [ DumpJSConsoleLogInStdErr ]
    612613imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-status.any.html [ DumpJSConsoleLogInStdErr ]
  • trunk/LayoutTests/http/tests/security/contentTypeOptions/invalid-content-type-options-allowed-expected.txt

    r230346 r230602  
    11CONSOLE MESSAGE: line 2: Executed script with MIME type: 'application/json'.
    2 CONSOLE MESSAGE: line 2: Executed script with MIME type: 'image/png'.
    32CONSOLE MESSAGE: line 2: Executed script with MIME type: 'text/html'.
    43CONSOLE MESSAGE: line 2: Executed script with MIME type: 'text/vbs'.
  • trunk/LayoutTests/http/tests/security/contentTypeOptions/invalid-content-type-options-allowed.html

    r142683 r230602  
    1111        var unscriptyMimeTypes = [
    1212            'application/json',
    13             'image/png',
    1413            'text/html',
    1514            'text/vbs',
  • trunk/LayoutTests/http/tests/security/contentTypeOptions/resources/script-with-header.pl

    r230346 r230602  
    88    print "Content-Type: " . $cgi->param('mime') . "\n";
    99}
    10 if ($cgi->param('options')) {
    11     print "X-Content-Type-Options: " . $cgi->param('options') . "\n";
    12 } else {
    13     print "X-Content-Type-Options: nosniff\n";
     10if (!$cgi->param('no-content-type-options')) {
     11        if ($cgi->param('options')) {
     12            print "X-Content-Type-Options: " . $cgi->param('options') . "\n";
     13        } else {
     14            print "X-Content-Type-Options: nosniff\n";
     15        }
    1416}
    1517print "\n";
  • trunk/LayoutTests/http/tests/security/cross-origin-cached-scripts-expected.txt

    r210546 r230602  
    66
    77Trying to load sequentially the same script from different origins.
    8 Test 1 PASS: Loaded script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js from localhost:8000 (crossOrigin=anonymous)
    9 Test 2 PASS: Did not load script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js from localhost:8080 (crossOrigin=anonymous)
    10 Test 3 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js from localhost:8080
    11 Test 4 PASS: Did not load script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js from localhost:8080 (crossOrigin=anonymous)
    12 Test 5 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js from localhost:8000 (crossOrigin=anonymous)
    13 Test 6 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js from localhost:8080 (crossOrigin=anonymous)
     8Test 1 PASS: Loaded script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript from localhost:8000 (crossOrigin=anonymous)
     9Test 2 PASS: Did not load script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript from localhost:8080 (crossOrigin=anonymous)
     10Test 3 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript from localhost:8080
     11Test 4 PASS: Did not load script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript from localhost:8080 (crossOrigin=anonymous)
     12Test 5 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js&contentType=text/javascript from localhost:8000 (crossOrigin=anonymous)
     13Test 6 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js&contentType=text/javascript from localhost:8080 (crossOrigin=anonymous)
    1414 
    1515 
  • trunk/LayoutTests/http/tests/security/cross-origin-cached-scripts-parallel-expected.txt

    r205908 r230602  
    66
    77Trying to load sequentially the same script from various origins.
    8 Test 1 PASS: Loaded script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000 from localhost:8000 (crossOrigin=anonymous)
    9 Test 2 PASS: Did not load script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000 from localhost:8080 (crossOrigin=anonymous)
    10 Test 3 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000 from localhost:8080
    11 Test 4 PASS: Did not load script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000 from localhost:8080 (crossOrigin=anonymous)
     8Test 1 PASS: Loaded script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript from localhost:8000 (crossOrigin=anonymous)
     9Test 2 PASS: Did not load script http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript from localhost:8080 (crossOrigin=anonymous)
     10Test 3 PASS: Loaded script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript from localhost:8080
     11Test 4 PASS: Did not load script http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript from localhost:8080 (crossOrigin=anonymous)
    1212 
    1313 
  • trunk/LayoutTests/http/tests/security/cross-origin-cached-scripts-parallel.html

    r205908 r230602  
    3737var iframeURL8080 = "http://localhost:8080/security/resources/cross-origin-cached-resource-iframe.html";
    3838
    39 var allow8000Script1 = "http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000";
    40 var allow8000Script2 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000";
     39var allow8000Script1 = "http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript";
     40var allow8000Script2 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&delay=1000&contentType=text/javascript";
    4141
    4242function firstTest()
  • trunk/LayoutTests/http/tests/security/cross-origin-cached-scripts.html

    r210546 r230602  
    3030var iframeURL8080 = "http://localhost:8080/security/resources/cross-origin-cached-resource-iframe.html";
    3131
    32 var allow8000Script1 = "http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js";
    33 var allow8000Script2 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js";
    34 var allow8000Script3 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js";
     32var allow8000Script1 = "http://127.0.0.1:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript";
     33var allow8000Script2 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8000&name=notify-loaded.js&contentType=text/javascript";
     34var allow8000Script3 = "http://127.0.0.1:8080/security/resources/allow-if-origin.php?allowCache&origin=*&name=notify-loaded.js&contentType=text/javascript";
    3535
    3636var counter = 0;
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r230594 r230602  
     12018-04-12  Daniel Bates  <dabates@apple.com>
     2
     3        Content-Type not enforced for <script> allows for XSS
     4        https://bugs.webkit.org/show_bug.cgi?id=184386
     5        <rdar://problem/39112268>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Update expected result now that we pass all sub tests.
     10
     11        * web-platform-tests/fetch/api/basic/block-mime-as-script-expected.txt:
     12
    1132018-04-12  Antoine Quint  <graouts@apple.com>
    214
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/basic/block-mime-as-script-expected.txt

    r222307 r230602  
    1 CONSOLE MESSAGE: line 1: Script loaded
    2 CONSOLE MESSAGE: line 1: Script loaded
    3 CONSOLE MESSAGE: line 1: Script loaded
    4 CONSOLE MESSAGE: line 1: Script loaded
    5 CONSOLE MESSAGE: line 1: Script loaded
    6 CONSOLE MESSAGE: line 1: Script loaded
    7 CONSOLE MESSAGE: line 1: Script loaded
    8 CONSOLE MESSAGE: line 1: Script loaded
    9 CONSOLE MESSAGE: line 1: Script loaded
    10 CONSOLE MESSAGE: line 1: Script loaded
    11 CONSOLE MESSAGE: line 1: Script loaded
    12 CONSOLE MESSAGE: line 1: Script loaded
    131
    14 FAIL Should fail loading non-empty script with text/csv MIME type assert_unreached: Unexpected load event Reached unreachable code
    15 FAIL Should fail loading non-empty script with audio/aiff MIME type assert_unreached: Unexpected load event Reached unreachable code
    16 FAIL Should fail loading non-empty script with audio/midi MIME type assert_unreached: Unexpected load event Reached unreachable code
    17 FAIL Should fail loading non-empty script with audio/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
    18 FAIL Should fail loading non-empty script with video/avi MIME type assert_unreached: Unexpected load event Reached unreachable code
    19 FAIL Should fail loading non-empty script with video/fli MIME type assert_unreached: Unexpected load event Reached unreachable code
    20 FAIL Should fail loading non-empty script with video/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
    21 FAIL Should fail loading non-empty script with image/jpeg MIME type assert_unreached: Unexpected load event Reached unreachable code
    22 FAIL Should fail loading non-empty script with image/gif MIME type assert_unreached: Unexpected load event Reached unreachable code
    23 FAIL Should fail loading non-empty script with image/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
    24 FAIL Should fail loading empty script with text/csv MIME type assert_unreached: Unexpected load event Reached unreachable code
    25 FAIL Should fail loading empty script with audio/aiff MIME type assert_unreached: Unexpected load event Reached unreachable code
    26 FAIL Should fail loading empty script with audio/midi MIME type assert_unreached: Unexpected load event Reached unreachable code
    27 FAIL Should fail loading empty script with audio/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
    28 FAIL Should fail loading empty script with video/avi MIME type assert_unreached: Unexpected load event Reached unreachable code
    29 FAIL Should fail loading empty script with video/fli MIME type assert_unreached: Unexpected load event Reached unreachable code
    30 FAIL Should fail loading empty script with video/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
    31 FAIL Should fail loading empty script with image/jpeg MIME type assert_unreached: Unexpected load event Reached unreachable code
    32 FAIL Should fail loading empty script with image/gif MIME type assert_unreached: Unexpected load event Reached unreachable code
    33 FAIL Should fail loading empty script with image/whatever MIME type assert_unreached: Unexpected load event Reached unreachable code
     2PASS Should fail loading non-empty script with text/csv MIME type
     3PASS Should fail loading non-empty script with audio/aiff MIME type
     4PASS Should fail loading non-empty script with audio/midi MIME type
     5PASS Should fail loading non-empty script with audio/whatever MIME type
     6PASS Should fail loading non-empty script with video/avi MIME type
     7PASS Should fail loading non-empty script with video/fli MIME type
     8PASS Should fail loading non-empty script with video/whatever MIME type
     9PASS Should fail loading non-empty script with image/jpeg MIME type
     10PASS Should fail loading non-empty script with image/gif MIME type
     11PASS Should fail loading non-empty script with image/whatever MIME type
     12PASS Should fail loading empty script with text/csv MIME type
     13PASS Should fail loading empty script with audio/aiff MIME type
     14PASS Should fail loading empty script with audio/midi MIME type
     15PASS Should fail loading empty script with audio/whatever MIME type
     16PASS Should fail loading empty script with video/avi MIME type
     17PASS Should fail loading empty script with video/fli MIME type
     18PASS Should fail loading empty script with video/whatever MIME type
     19PASS Should fail loading empty script with image/jpeg MIME type
     20PASS Should fail loading empty script with image/gif MIME type
     21PASS Should fail loading empty script with image/whatever MIME type
    3422PASS Should load script with text/html MIME type
    3523PASS Should load script with text/plain MIME type
  • trunk/Source/WebCore/ChangeLog

    r230595 r230602  
     12018-04-12  Daniel Bates  <dabates@apple.com>
     2
     3        Content-Type not enforced for <script> allows for XSS
     4        https://bugs.webkit.org/show_bug.cgi?id=184386
     5        <rdar://problem/39112268>
     6
     7        Reviewed by Brady Eidson.
     8
     9        As per the Fetch standard, <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?> (16 March 2018),
     10        we should block JavaScript scripts that are served with MIME type text/csv, or a MIME type
     11        that begins with "audio/", "image/" or "video/".
     12
     13        As a side benefit of this change we now set the destination property [1] on preload requests.
     14
     15        [1] <https://fetch.spec.whatwg.org/#concept-request-destination>
     16
     17        Tests: http/tests/security/script-with-banned-mimetype.html
     18               http/tests/workers/worker-importScripts-banned-mimetype.html
     19
     20        * Sources.txt: Add file FetchIdioms.cpp.
     21        * WebCore.xcodeproj/project.pbxproj: Add files FetchIdioms.{cpp, h}.
     22        * dom/LoadableClassicScript.cpp:
     23        (WebCore::LoadableClassicScript::notifyFinished): Check the MIME type of the response and
     24        block the script if applicable.
     25        * dom/LoadableScript.h: Add error type MIMEType.
     26        * loader/FetchIdioms.cpp: Added.
     27        (WebCore::shouldBlockResponseDueToMIMEType): Implements the "Should response to request be blocked
     28        due to its MIME type?" algorithm from the Fetch standard.
     29        * loader/FetchIdioms.h: Added.
     30        * loader/FetchOptions.h:
     31        (WebCore::isScriptLikeDestination): Implements the definition of "script like" as per <https://fetch.spec.whatwg.org/#request-destination-script-like>.
     32        * loader/cache/CachedResourceLoader.cpp:
     33        (WebCore::CachedResourceLoader::requestImage): Removed logic to set the destination property as
     34        CachedResourceLoader::requestResource() is now responsible for doing this.
     35        (WebCore::CachedResourceLoader::requestFont): Ditto.
     36        (WebCore::CachedResourceLoader::requestTextTrack): Ditto.
     37        (WebCore::CachedResourceLoader::requestCSSStyleSheet): Ditto.
     38        (WebCore::CachedResourceLoader::requestScript): Ditto.
     39        (WebCore::CachedResourceLoader::requestXSLStyleSheet): Ditto.
     40        (WebCore::CachedResourceLoader::requestMedia): Update comment to express that we should assert
     41        that the destination property is either video or audio.
     42        (WebCore::CachedResourceLoader::requestIcon): Remove logic to set the destination property as
     43        CachedResourceLoader::requestResource() is now responsible for doing this.
     44        (WebCore::CachedResourceLoader::requestRawResource): Removed assertion as this function is used to
     45        load many kinds of requests that have different destination properties. The caller is responsible
     46        for setting the appropriate destintion property.
     47        (WebCore::CachedResourceLoader::requestMainResource): Remove logic to set the destination property
     48        as CachedResourceLoader::requestResource() is now responsible for doing this.
     49        (WebCore::destinationForType): Helper function that maps CachedResource::Type to FetchOptions::Destination.
     50        (WebCore::CachedResourceLoader::requestResource): Set the destination property on the request if not
     51        already set.
     52        * loader/cache/CachedResourceLoader.h: Segregate requestRawResource() from the other request functions
     53        and add a comment to explain what it is used for.
     54        * workers/Worker.cpp:
     55        (WebCore::Worker::create):
     56        * workers/WorkerScriptLoader.cpp:
     57        (WebCore::WorkerScriptLoader::loadSynchronously): Set the destination property to FetchOptions::Destination::Script
     58        and store it in an instance variable as we will need to reference it once we receive the HTTP response.
     59        Note that this function is only used to support the Web API importScripts().
     60        (WebCore::WorkerScriptLoader::loadAsynchronously): Store the passed destination property in an
     61        instance as we will need to reference it once we receive the HTTP response.
     62        (WebCore::WorkerScriptLoader::didReceiveResponse): Check the MIME type of the response and
     63        block the script if applicable.
     64        * workers/WorkerScriptLoader.h:
     65        * workers/service/ServiceWorkerJob.cpp:
     66        (WebCore::ServiceWorkerJob::fetchScriptWithContext): Set the destination property to FetchOptions::Destination::Serviceworker.
     67
    1682018-04-12  Antoine Quint  <graouts@apple.com>
    269
  • trunk/Source/WebCore/Sources.txt

    r230226 r230602  
    12251225loader/FrameLoader.cpp
    12261226loader/FrameLoaderStateMachine.cpp
     1227loader/FetchIdioms.cpp
    12271228loader/HTTPHeaderField.cpp
    12281229loader/HistoryController.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r230544 r230602  
    40604060                CE7B2DB51586ABAD0098B3FA /* TextAlternativeWithRange.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7B2DB11586ABAD0098B3FA /* TextAlternativeWithRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
    40614061                CE7E17831C83A49100AD06AF /* ContentSecurityPolicyHash.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
     4062                CEBB8C3320786DCB00039547 /* FetchIdioms.h in Headers */ = {isa = PBXBuildFile; fileRef = CEBB8C3120786DCB00039547 /* FetchIdioms.h */; };
    40624063                CECADFC7153778FF00E37068 /* DictationAlternative.h in Headers */ = {isa = PBXBuildFile; fileRef = CECADFC3153778FF00E37068 /* DictationAlternative.h */; settings = {ATTRIBUTES = (Private, ); }; };
    40634064                CECADFC9153778FF00E37068 /* DictationCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = CECADFC5153778FF00E37068 /* DictationCommand.h */; };
     
    1335013351                CE7B2DB21586ABAD0098B3FA /* TextAlternativeWithRange.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextAlternativeWithRange.mm; sourceTree = "<group>"; };
    1335113352                CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContentSecurityPolicyHash.h; path = csp/ContentSecurityPolicyHash.h; sourceTree = "<group>"; };
     13353                CEBB8C3120786DCB00039547 /* FetchIdioms.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FetchIdioms.h; sourceTree = "<group>"; };
     13354                CEBB8C3220786DCB00039547 /* FetchIdioms.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FetchIdioms.cpp; sourceTree = "<group>"; };
    1335213355                CECADFC2153778FF00E37068 /* DictationAlternative.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DictationAlternative.cpp; sourceTree = "<group>"; };
    1335313356                CECADFC3153778FF00E37068 /* DictationAlternative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DictationAlternative.h; sourceTree = "<group>"; };
     
    2391023913                                B255990D0D00D8B900BB825C /* EmptyClients.h */,
    2391123914                                414DEDE51F9FE9150047C40D /* EmptyFrameLoaderClient.h */,
     23915                                CEBB8C3220786DCB00039547 /* FetchIdioms.cpp */,
     23916                                CEBB8C3120786DCB00039547 /* FetchIdioms.h */,
    2391223917                                41AD75391CEF6BCE00A31486 /* FetchOptions.h */,
    2391323918                                656D37230ADBA5DE00A4554D /* FormState.cpp */,
     
    2753527540                                4129C9971F59B963009D7403 /* FetchBodySource.h in Headers */,
    2753627541                                41D129DB1F3D143800D15E47 /* FetchHeaders.h in Headers */,
     27542                                CEBB8C3320786DCB00039547 /* FetchIdioms.h in Headers */,
    2753727543                                4161E2D51FE48DC500EC2E96 /* FetchLoader.h in Headers */,
    2753827544                                517A53581F5889E800DCDC0A /* FetchLoaderClient.h in Headers */,
  • trunk/Source/WebCore/dom/LoadableClassicScript.cpp

    r230346 r230602  
    2727#include "LoadableClassicScript.h"
    2828
     29#include "FetchIdioms.h"
    2930#include "ScriptElement.h"
    3031#include "ScriptSourceCode.h"
     
    9697    }
    9798
     99    if (!m_error && shouldBlockResponseDueToMIMEType(m_cachedScript->response(), m_cachedScript->options().destination)) {
     100        m_error = Error {
     101            ErrorType::MIMEType,
     102            ConsoleMessage {
     103                MessageSource::Security,
     104                MessageLevel::Error,
     105                makeString("Refused to execute ", m_cachedScript->url().stringCenterEllipsizedToLength(), " as script because ", m_cachedScript->response().mimeType(), " is not a script MIME type.")
     106            }
     107        };
     108    }
     109
    98110    if (!m_error && !resource.errorOccurred() && !matchIntegrityMetadata(resource, m_integrity)) {
    99111        m_error = Error {
  • trunk/Source/WebCore/dom/LoadableScript.h

    r228218 r230602  
    4141        CachedScript,
    4242        CrossOriginLoad,
     43        MIMEType,
    4344        Nosniff,
    4445        FailedIntegrityCheck,
  • trunk/Source/WebCore/loader/FetchOptions.h

    r225294 r230602  
    8989}
    9090
     91inline bool isScriptLikeDestination(FetchOptions::Destination destination)
     92{
     93    return destination == FetchOptions::Destination::Script
     94        || destination == FetchOptions::Destination::Serviceworker
     95        || destination == FetchOptions::Destination::Worker;
     96}
     97
    9198}
    9299
  • trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp

    r230489 r230602  
    198198ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request)
    199199{
    200     request.setDestinationIfNotSet(FetchOptions::Destination::Image);
    201200    if (Frame* frame = this->frame()) {
    202201        if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) {
     
    216215ResourceErrorOr<CachedResourceHandle<CachedFont>> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG)
    217216{
    218     request.setDestinationIfNotSet(FetchOptions::Destination::Font);
    219217#if ENABLE(SVG_FONTS)
    220218    if (isSVG)
     
    229227ResourceErrorOr<CachedResourceHandle<CachedTextTrack>> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request)
    230228{
    231     request.setDestinationIfNotSet(FetchOptions::Destination::Track);
    232229    return castCachedResourceTo<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, WTFMove(request)));
    233230}
     
    236233ResourceErrorOr<CachedResourceHandle<CachedCSSStyleSheet>> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request)
    237234{
    238     request.setDestinationIfNotSet(FetchOptions::Destination::Style);
    239235    return castCachedResourceTo<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, WTFMove(request)));
    240236}
     
    270266ResourceErrorOr<CachedResourceHandle<CachedScript>> CachedResourceLoader::requestScript(CachedResourceRequest&& request)
    271267{
    272     request.setDestinationIfNotSet(FetchOptions::Destination::Script);
    273268    return castCachedResourceTo<CachedScript>(requestResource(CachedResource::Script, WTFMove(request)));
    274269}
     
    277272ResourceErrorOr<CachedResourceHandle<CachedXSLStyleSheet>> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request)
    278273{
    279     request.setDestinationIfNotSet(FetchOptions::Destination::Xslt);
    280274    return castCachedResourceTo<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, WTFMove(request)));
    281275}
     
    296290ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestMedia(CachedResourceRequest&& request)
    297291{
    298     // FIXME: Set destination to either audio or video.
     292    // FIXME: Assert request.options().destination is FetchOptions::Destination::{Audio, Video}.
    299293    return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::MediaResource, WTFMove(request)));
    300294}
     
    302296ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestIcon(CachedResourceRequest&& request)
    303297{
    304     request.setDestinationIfNotSet(FetchOptions::Destination::Image);
    305298    return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Icon, WTFMove(request)));
    306299}
     
    308301ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request)
    309302{
    310     ASSERT(request.options().destination == FetchOptions::Destination::EmptyString || request.options().serviceWorkersMode == ServiceWorkersMode::None);
    311303    return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::RawResource, WTFMove(request)));
    312304}
     
    320312ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request)
    321313{
    322     request.setDestinationIfNotSet(FetchOptions::Destination::Document);
    323314    return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::MainResource, WTFMove(request)));
    324315}
     
    739730}
    740731
     732static FetchOptions::Destination destinationForType(CachedResource::Type type)
     733{
     734    switch (type) {
     735    case CachedResource::MainResource:
     736    case CachedResource::SVGDocumentResource:
     737        return FetchOptions::Destination::Document;
     738    case CachedResource::ImageResource:
     739    case CachedResource::Icon:
     740        return FetchOptions::Destination::Image;
     741    case CachedResource::CSSStyleSheet:
     742        return FetchOptions::Destination::Style;
     743    case CachedResource::Script:
     744        return FetchOptions::Destination::Script;
     745    case CachedResource::FontResource:
     746#if ENABLE(SVG_FONTS)
     747    case CachedResource::SVGFontResource:
     748#endif
     749        return FetchOptions::Destination::Font;
     750#if ENABLE(XSLT)
     751    case CachedResource::XSLStyleSheet:
     752        return FetchOptions::Destination::Xslt;
     753#endif
     754#if ENABLE(VIDEO_TRACK)
     755    case CachedResource::TextTrackResource:
     756        return FetchOptions::Destination::Track;
     757#endif
     758#if ENABLE(APPLICATION_MANIFEST)
     759    case CachedResource::ApplicationManifest:
     760        return FetchOptions::Destination::Manifest;
     761#endif
     762    case CachedResource::Beacon:
     763    case CachedResource::LinkPrefetch:
     764    case CachedResource::RawResource:
     765    case CachedResource::MediaResource:
     766        // The caller is responsible for setting the appropriate destination.
     767        return FetchOptions::Destination::EmptyString;
     768    }
     769}
     770
    741771ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
    742772{
     773    request.setDestinationIfNotSet(destinationForType(type));
     774
    743775    // Entry point to https://fetch.spec.whatwg.org/#main-fetch.
    744776    std::unique_ptr<ResourceRequest> originalRequest;
  • trunk/Source/WebCore/loader/cache/CachedResourceLoader.h

    r229563 r230602  
    8888    ResourceErrorOr<CachedResourceHandle<CachedRawResource>> requestIcon(CachedResourceRequest&&);
    8989    ResourceErrorOr<CachedResourceHandle<CachedRawResource>> requestBeaconResource(CachedResourceRequest&&);
    90     ResourceErrorOr<CachedResourceHandle<CachedRawResource>> requestRawResource(CachedResourceRequest&&);
    9190    ResourceErrorOr<CachedResourceHandle<CachedRawResource>> requestMainResource(CachedResourceRequest&&);
    9291    ResourceErrorOr<CachedResourceHandle<CachedSVGDocument>> requestSVGDocument(CachedResourceRequest&&);
     
    101100    ResourceErrorOr<CachedResourceHandle<CachedApplicationManifest>> requestApplicationManifest(CachedResourceRequest&&);
    102101#endif
     102
     103    // Called to load Web Worker main script, Service Worker main script, importScripts(), XHR,
     104    // EventSource, Fetch, and App Cache.
     105    ResourceErrorOr<CachedResourceHandle<CachedRawResource>> requestRawResource(CachedResourceRequest&&);
    103106
    104107    // Logs an access denied message to the console for the specified URL.
  • trunk/Source/WebCore/workers/Worker.cpp

    r230374 r230602  
    108108    options.cache = FetchOptions::Cache::Default;
    109109    options.redirect = FetchOptions::Redirect::Follow;
     110    options.destination = FetchOptions::Destination::Worker;
    110111    worker->m_scriptLoader->loadAsynchronously(context, WTFMove(request), WTFMove(options), contentSecurityPolicyEnforcement, ServiceWorkersMode::All, worker);
    111112    return WTFMove(worker);
  • trunk/Source/WebCore/workers/WorkerScriptLoader.cpp

    r230374 r230602  
    2929
    3030#include "ContentSecurityPolicy.h"
     31#include "FetchIdioms.h"
    3132#include "ResourceResponse.h"
    3233#include "ScriptExecutionContext.h"
     
    5051
    5152    m_url = url;
     53    m_destination = FetchOptions::Destination::Script;
    5254
    5355    std::unique_ptr<ResourceRequest> request(createResourceRequest(initiatorIdentifier));
     
    6668    options.sendLoadCallbacks = SendCallbacks;
    6769    options.contentSecurityPolicyEnforcement = contentSecurityPolicyEnforcement;
     70    options.destination = m_destination;
    6871#if ENABLE(SERVICE_WORKER)
    6972    options.serviceWorkersMode = workerGlobalScope.isServiceWorkerGlobalScope() ? ServiceWorkersMode::None : ServiceWorkersMode::All;
     
    7881    m_client = &client;
    7982    m_url = scriptRequest.url();
     83    m_destination = fetchOptions.destination;
    8084
    8185    ASSERT(scriptRequest.httpMethod() == "GET");
     
    133137    }
    134138
     139    if (shouldBlockResponseDueToMIMEType(response, m_destination)) {
     140        String message = makeString("Refused to execute ", response.url().stringCenterEllipsizedToLength(), " as script because ", response.mimeType(), " is not a script MIME type.");
     141        m_error = ResourceError { errorDomainWebKitInternal, 0, response.url(), message, ResourceError::Type::General };
     142        m_failed = true;
     143        return;
     144    }
     145
    135146    m_responseURL = response.url();
    136147    m_responseMIMEType = response.mimeType();
  • trunk/Source/WebCore/workers/WorkerScriptLoader.h

    r230374 r230602  
    2828
    2929#include "ContentSecurityPolicyResponseHeaders.h"
     30#include "FetchOptions.h"
    3031#include "ResourceError.h"
    3132#include "ResourceRequest.h"
     
    9293    URL m_responseURL;
    9394    String m_responseMIMEType;
     95    FetchOptions::Destination m_destination;
    9496    ContentSecurityPolicyResponseHeaders m_contentSecurityPolicy;
    9597    unsigned long m_identifier { 0 };
  • trunk/Source/WebCore/workers/service/ServiceWorkerJob.cpp

    r230374 r230602  
    103103    options.cache = cachePolicy;
    104104    options.redirect = FetchOptions::Redirect::Error;
     105    options.destination = FetchOptions::Destination::Serviceworker;
    105106    m_scriptLoader->loadAsynchronously(context, WTFMove(request), WTFMove(options), ContentSecurityPolicyEnforcement::DoNotEnforce, ServiceWorkersMode::None, *this);
    106107}
Note: See TracChangeset for help on using the changeset viewer.