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.