Bug 155761 - Origin header is not included in CORS requests for preloaded cross-origin resources
Summary: Origin header is not included in CORS requests for preloaded cross-origin res...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Page Loading (show other bugs)
Version: Safari 9
Hardware: All All
: P2 Normal
Assignee: youenn fablet
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-03-22 11:46 PDT by Josh Dover
Modified: 2016-06-14 01:00 PDT (History)
13 users (show)

See Also:


Attachments
Patch (15.01 KB, patch)
2016-06-09 05:33 PDT, youenn fablet
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews117 for mac-yosemite (1.42 MB, application/zip)
2016-06-09 06:31 PDT, Build Bot
no flags Details
Updating test (15.94 KB, patch)
2016-06-10 05:44 PDT, youenn fablet
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Josh Dover 2016-03-22 11:46:38 PDT
According to the CORS specification [1], all CORS requests must include the 'Origin' header. Many server and CDN implementations will not respond with any of the appropriate CORS headers if the Origin is not specified. When a resource is referenced by a `<link>` tag, WebKit does not include the Origin in the request. While WebKit does still accept the resource as valid, it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet (it will be `null` for tainted stylesheets).

I confirmed this is the case via `tcpdump` with Safari 9.0.3 (11601.4.4) and got the same results in WebKit nightly r198522. Example tag used:

```
<link href="//mycdn.com/style.css" rel="stylesheet" type="text/css" media="all" crossorigin="use-credentials">
```

Recorded Safari request, note the missing Origin header:
```
13:39:48.413187 IP 10.0.1.136.50969 > 69.172.201.208.http: Flags [P.], seq 2207105597:2207105953, ack 4251157642, win 65535, length 356: HTTP: GET /style.css HTTP/1.1
$...7.8...y...E.....@.@...
...E......P...=.c..P.......GET /style.css HTTP/1.1
Host: mycdn.com
Accept-Encoding: gzip, deflate
Connection: keep-alive
Accept: text/css,*/*;q=0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4
Accept-Language: en-us
Referer: http://my.dev:5000/designer/181021729
Cache-Control: max-age=0
```

Recorded Chrome request, note the Origin header:
```
13:38:33.594253 IP 10.0.1.136.50856 > 69.172.201.208.http: Flags [P.], seq 4043612470:4043612873, ack 2525948378, win 65535, length 403: HTTP: GET /style.css HTTP/1.1
$...7.8...y...E.....@.@...
...E......P...6....P.......GET /style.css HTTP/1.1
Host: mycdn.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/css,*/*;q=0.1
Origin: http://my.dev:5000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Referer: http://my.dev:5000/designer/181021729
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
```

I've confirmed that Firefox + Chrome (example above) both handle this correctly. Safari also behaves the same if the `crossorigin` attribute set to 'use-credentials' or 'anonymous'.

This could be a security threat since some developers may naively opt to send `Access-Control-Allow-Origin: *` as a workaround for this Safari-specific issue. Additionally, this poses an issue for anyone using a CDN that has limited CORS configuration options. For instance, AWS S3 will not send CORS headers if there is no Origin header, regardless of configuration.


[1] http://www.w3.org/TR/cors/#origin-request-header
Comment 1 Josh Dover 2016-03-22 11:48:40 PDT
This is also a bug that many others have experienced. A good list of related issues on github: https://github.com/photonstorm/phaser/issues/1355#issuecomment-64144909
Comment 2 Radar WebKit Bug Importer 2016-03-24 18:28:26 PDT
<rdar://problem/25351850>
Comment 3 Josh Dover 2016-06-06 12:15:03 PDT
Anyone looking for a SAFE workaround should not be using `Access-Control-Allow-Origin: *`. What I used that worked:

- Find the CSSStyleSheet in the `tainted` state (`styleSheet.cssRules` will be null).
- Make an XHR for the stylesheet's `href`
- Add a new <style> node to the document's head with the contents of the XHR.
- Optionally, remove the original style tag

This method will be safe since you are still relying on the browser's CORS protections when you make the XHR.
Comment 4 Alexey Proskuryakov 2016-06-06 14:01:14 PDT
> While WebKit does still accept the resource as valid, it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet

Do you have a complete test case demonstrating that CSSRuleList cannot be accessed from a script in WebKit?
Comment 5 youenn fablet 2016-06-09 02:20:58 PDT
I also confirm the issue.
Comment 6 youenn fablet 2016-06-09 05:33:55 PDT
Created attachment 280913 [details]
Patch
Comment 7 youenn fablet 2016-06-09 05:37:37 PDT
(In reply to comment #6)
> Created attachment 280913 [details]
> --no-review

This patch makes preload scanner use CORS mode for all preloaded resources that have crossorigin attribute, not only CSS stylesheets.
HTMLLinkElement is also updated to make use of the attribute.
Comment 8 Build Bot 2016-06-09 06:31:50 PDT
Comment on attachment 280913 [details]
Patch

Attachment 280913 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/1472290

New failing tests:
editing/selection/selection-in-iframe-removed-crash.html
Comment 9 Build Bot 2016-06-09 06:31:54 PDT
Created attachment 280917 [details]
Archive of layout-test-results from ews117 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews117  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 10 youenn fablet 2016-06-09 08:16:30 PDT
(In reply to comment #8)
> Comment on attachment 280913 [details]
> Patch
> 
> Attachment 280913 [details] did not pass mac-debug-ews (mac):
> Output: http://webkit-queues.webkit.org/results/1472290
> 
> New failing tests:
> editing/selection/selection-in-iframe-removed-crash.html

Failing test seems not relevant to the changes.
I'll check further though.
Comment 11 Ryan Haddad 2016-06-09 09:11:30 PDT
(In reply to comment #10)
> (In reply to comment #8)
> > Comment on attachment 280913 [details]
> > Patch
> > 
> > Attachment 280913 [details] did not pass mac-debug-ews (mac):
> > Output: http://webkit-queues.webkit.org/results/1472290
> > 
> > New failing tests:
> > editing/selection/selection-in-iframe-removed-crash.html
> 
> Failing test seems not relevant to the changes.
> I'll check further though.

The failure may be related to http://trac.webkit.org/changeset/201823
Comment 12 Alex Christensen 2016-06-09 09:39:59 PDT
> it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet (it will be `null` for tainted stylesheets)
This doesn't seem to be tested in the test.  Is this really the case?  If we can just get the computed style of an element effected by the stylesheet, what is the point of not being able to access the CSSRuleList?
Comment 13 youenn fablet 2016-06-09 11:31:31 PDT
(In reply to comment #12)
> > it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet (it will be `null` for tainted stylesheets)
> This doesn't seem to be tested in the test.  Is this really the case?  If we
> can just get the computed style of an element effected by the stylesheet,
> what is the point of not being able to access the CSSRuleList?

I can add such test but I guess there are already tests somewhere ensuring that tainted stylesheets are not accessible in details through JS, similarly to tainted images in canvas.
Comment 14 youenn fablet 2016-06-10 05:39:49 PDT
(In reply to comment #13)
> (In reply to comment #12)
> > > it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet (it will be `null` for tainted stylesheets)
> > This doesn't seem to be tested in the test.  Is this really the case?  If we
> > can just get the computed style of an element effected by the stylesheet,
> > what is the point of not being able to access the CSSRuleList?
> 
> I can add such test but I guess there are already tests somewhere ensuring
> that tainted stylesheets are not accessible in details through JS, similarly
> to tainted images in canvas.

I was too optimistic.
Access to cssRules is governed by CSSStyleSheet::canAccessRules.
This routine is checking whether the stylesheet URL is same-origin as the owner document origin.
It does not take into account CORS.

This issue being different and specific to CSS, I think it would be best tackled as a follow-up bug.
I'll upload a patch with an updated test.
Comment 15 youenn fablet 2016-06-10 05:44:42 PDT
Created attachment 281006 [details]
Updating test
Comment 16 youenn fablet 2016-06-10 05:45:33 PDT
(In reply to comment #15)
> Created attachment 281006 [details]
> Updating test

To show the cssRules specific issue, one can just uncomment line 25 of the new test.
Comment 17 youenn fablet 2016-06-10 09:31:37 PDT
(In reply to comment #16)
> (In reply to comment #15)
> > Created attachment 281006 [details]
> > Updating test
> 
> To show the cssRules specific issue, one can just uncomment line 25 of the
> new test.

Note also that the patch is not enforcing full cross origin checks yet.
Loading should fail if cross origin is set but Allow-Control-Allow-Origin is not in the response headers.
This is the target of another bug.
Comment 18 WebKit Commit Bot 2016-06-10 11:15:40 PDT
Comment on attachment 281006 [details]
Updating test

Clearing flags on attachment: 281006

Committed r201930: <http://trac.webkit.org/changeset/201930>
Comment 19 WebKit Commit Bot 2016-06-10 11:15:46 PDT
All reviewed patches have been landed.  Closing bug.
Comment 20 youenn fablet 2016-06-14 01:00:15 PDT
(In reply to comment #14)
> (In reply to comment #13)
> > (In reply to comment #12)
> > > > it is marked as 'tainted' which prevents JavaScript on the page from accessing the CSSRuleList object on the CSSStyleSheet (it will be `null` for tainted stylesheets)
> > > This doesn't seem to be tested in the test.  Is this really the case?  If we
> > > can just get the computed style of an element effected by the stylesheet,
> > > what is the point of not being able to access the CSSRuleList?
> > 
> > I can add such test but I guess there are already tests somewhere ensuring
> > that tainted stylesheets are not accessible in details through JS, similarly
> > to tainted images in canvas.
> 
> I was too optimistic.
> Access to cssRules is governed by CSSStyleSheet::canAccessRules.
> This routine is checking whether the stylesheet URL is same-origin as the
> owner document origin.
> It does not take into account CORS.
> 
> This issue being different and specific to CSS, I think it would be best
> tackled as a follow-up bug.
> I'll upload a patch with an updated test.

I filed bug 158728 for that purpose.