CORS: an insufficient solution for same-origin restrictions

UPDATE2: The main criticism I have received is that people will click “allow” without understanding the security implications of this action. I don’t know yet how to mitigate this risk, but the statu quo is not an option as the problem already exists: one can already develop cross-browser addons able to perform cross-domain requests after a single-click installation.

UPDATE1: I’ve added technical details of how “user enabled CORS” would work.

The same origin policy is the most fundamental principle of security built in web browsers: a web page cannot access the content of websites hosted on other (sub)domains. Developers came up with JSONP, and XHR2 introduced CORS to work around the strictness of this policy. Both mechanisms need to be implemented by API/content providers (Twitter, Facebook, Google Maps, Flickr, …) and leave no power to third party web devs and users. This severely impedes the hackability of the Web.

I want to see the Web succeed at providing a cross-platform (mobile) apps environment, and stand competition with other proprietary environments. I’m thrilled by the progress of WebAPIs lately, and yet, I find myself often stuck when trying to create basic webapps.

Same Origin Restrictions

While experimenting with foreignr, an offline enabled client for Google Maps, I found myself limited by the impossibility to dynamically cache images across different origins. I wrote about it, and ultimately had to write a client/server library to abuse appCache through iframes (see mediaCache).
Recently I started experimenting with a Twitter client and soon hit the limitations of their JSONP API: every request (save public read) have to be sent with an oAuth signature as a custom header. Since JSONP is transported using a <script> tag instead of an XHR object, custom headers aren’t available.

CORS to the rescue?

Both problems would be solved if Twitter and Google Maps added a simple header to their API: Access-Control-Allow-Origin: *. Unfortunately they don’t, and show no sign of willingness to do so. In fact, despite efforts to promote widespread adoption of CORS, only a very limited number of (small) sites implement it. Many websites provide JSONP APIs, but CORS has much more use cases:

  • uploading pictures to an image hosting website (already allowed by imgur.com),
  • storing images or any media locally for offline use,
  • enabling cross-origin oAuth requests,
  • using images or videos as WebGL textures,
  • processing image data inside a canvas element…

Doing any of that without CORS requires to use a server acting as a same-origin proxy to the targeted APIs/resources. What a costly waste. Meanwhile, every single desktop and mobile SDKs allow developers to do such things, even when based on Web technologies such as Blackberry WebWorks, WebOS SDK or PhoneGap.

CORS and beyond

prompt UI of user enabled CORS
Just like we allow or will allow webapps to do such sensitive operations as geolocating the user, or accessing one’s phonebook after obtaining permission, it should be possible to use public features and resources of one particular website on a different origin (i.e. what a user can access without being logged in).
The security implications are important, but one could imagine an opt-out Access-Control-Prevent-Origin: * header, blacklists and other ways to mitigate the risks.

This mechanism would be no different than current XHRs:

// script on http://example.com
var xhr = new XMLHttpRequest();
xhr.open("GET",
  "http://api.twitter.com/1/statuses/home_timeline.json", true);
xhr.setRequestHeader("Authorization", oAuth);
xhr.onreadystatechange = function ( e ) {
  ...
};
xhr.send();

Executing previous code would prompt the user for permission and a request would be sent only if permission is given, otherwise it would fail with a usual cross-origin error. Cookies associated with the target address would not be sent with this request; it would still have to be authorized through oAuth, even if the user is logged in to Twitter.

Moving forward

I can keep on asking API providers to add CORS headers, I will, hoping that one day it will become as widespread as JSONP. But there will always be new APIs that don’t add it immediately, and there will always be companies that don’t listen to their users. And until Web developers have a way to work around the same origin policy, the Web will not be a suitable platform for many apps.

I’d like to hear about other alternatives to CORS and ways to mitigate the risks. If you’ve got an idea, speak up, and let’s move the web forward.

16 thoughts on “CORS: an insufficient solution for same-origin restrictions

  1. David Bruant

    I like the snapshot and your proposal.
    I have some comments though:
    “it should be possible to use public features and resources of one particular website on a different origin (i.e. what a user can access without being logged in).”
    => “public features (what a user can access without being logged in)”
    I don’t think there is an automated way to make the difference between what requires authentication and what does not.
    Also, I think

    “one particular website”
    => Why stop at one? The user could be asked for each different origins the web page is trying to access to and the user could accept/refuse them individually (UI may be a difficult issue here, but that’s another problem).

    Among the things Mark Miller talks about in the distributed system is that a way for an server to authorize an operation is to hand a particular URL to a client. This URL will be unguessable (that’s the best we can do), so cryptographically safe. All people with access to this URL can access the content. Example:
    If I want you to access my private Facebook photos (without you having to hack Facebook :-p), I would just hand you a URL. And there is no need to authenticate. Either you have the URL or you don’t. Knowledge of the URL is all you need to access the content. Identity is only used on a bootstrap phase (“initial” repartition of URLs among users).
    It would probably even be possible for clients to revoke a URL by telling the server “I don’t want this particular URL to work anymore”. Of course, the service to do that would be accessible with URL that you wouldn’t share with anyone :-p

    1. louisremi Post author

      Hey David,

      Regarding the distinction between what requires authentication and what doesn’t, I was only thinking cookies shouldn’t be sent with requests. The security risk would be far too high otherwise.
      When I’m saying “one particular website”, I mean one website at a time. You could prompt the user multiple time for multiple domains.

      I watched the video you linked for about 20mins and I’m glad you give me a summary. So how do you prevent a particular authorized viewer to compromise the page when sharing his link?

      1. David Bruant

        “I watched the video you linked for about 20mins and I’m glad you give me a summary.”
        => :-p The content of this video is very dense. I had to watch it several times to fully get it. I even went to Brussels in October to see Mark Miller give this talk (and another one also given by him). It’s dense, hard to understand, but if applied properly, it would provide a better security and make the Same Origin Policy plain useless (in the network case. The cross-frame communication frame is different).

        “So how do you prevent a particular authorized viewer to compromise the page when sharing his link?”
        => You do not. When you share a link with someone, you trust this person to do whatever s/he wants with it. If you do not trust this person, don’t share the link.
        If you have limited trust, create another resource (referenced by another URL) and share this URL with restricted content/capability.

        Mark Miller mentioned something about Belay: https://sites.google.com/site/belayresearchproject/ I haven’t dug deeply into it yet, but have a look at it, it looks promising.
        https://sites.google.com/site/belayresearchproject/team-blog/demoandtalkatmozilla

        If you want to talk about it more, please answer by e-mail.

        1. louisremi Post author

          So that’s what’s implemented on Youtube and Google Docs sharing features, at least.
          I’d like to see it more widespread, but I’m not sure this would solve the cross-origin oAuth problem.

  2. Anne

    Hey, so we cannot ask the user for this. They will just click yes and then by doing so expose all the intranet servers they are connected to and servers they are IP-authenticated with. That is the reason servers have to opt-in and I do not see any way around that.

    I hope CORS will become more widespread once it’s more widely deployed in browsers. Advocacy with respect to its security benefits would probably help too. Need to find some free time.

    1. David Bruant

      Caniuse (http://caniuse.com/#search=cors) says that effectively, 85% of users have a web browser which can leverage CORS. I think CORS can be considered as “widely deployed in browsers”.

      “Hey, so we cannot ask the user for this. They will just click yes and then by doing so expose all the intranet servers they are connected to and servers they are IP-authenticated with.”
      => Web browsers can provide scary enough user messages to scare uneducated users out and make the UI annoying to discourage the user to say yes. “are you really sure?”
      The message could be specific for intranet servers (if accessed through addresses in http://tools.ietf.org/html/rfc1918 ). “you’re risking to compromise your company or local network”.
      Companies could also decide to install web browsers with this feature disabled by default.

      1. louisremi Post author

        Yes, it would be easy to add a pref that would prevent cross-origin xhr and never prompt for permission.

        1. FremyCompany

          Microsoft had such an option in IE6, maybe IE7. However, users tend to accept and don’t really understand how it could hurt them.

      2. Henri Sivonen

        Intranets aren’t limited to RFC 1918 addresses. Moreover, there are IP-authenticated extranets, where company A buys a service from a company B and company B uses the IP range assigned to the office of A for authentication.

        One might argue that this is a totally wrong way to do authentication, but the fact is that it happens and it would be bad if browsers broke that sort of arrangements by default and exposed confidential data.

        “By default” is a key part of the previous sentence, so your suggestion of companies having to disable browser features when installing browsers isn’t an appropriate solution. A random IT person at random company can’t know the security implications of the latest browser features, so it’s unreasonable to require the random IT person to keep track of the browser features and disable them. Besides, it’s a terrible start for designing a new feature to suggest that some people disable it.

  3. David Bruant

    Once, I had the idea that we could an option to XHR to prevent sending cookies. As it turns it may not be enough. Thread started at:
    http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/1054.html

    However, I am realizing that what you’re proposing (browser-level dialog to allow one domain to access another domain) is a very interesting idea to prevent the second issue Boris Zbarsky came up with.
    Have you filed a bug on Firefox to propose this? If not, I will.

    1. louisremi Post author

      Interesting thread!
      I haven’t made any proposition yet. I want to demonstrate how easy it is to send privileged XHRs using addons anyway.

  4. Andrew

    I guess I don’t see the big deal with using a proxy rewrite. We’ve been doing it this way since browsers started preventing XSS and it is rock solid and 100% universally compatible.
    Whats more is that using a proxy also allows you to keep your session secure. I read this article and I know there is a desire for a software-only solution instead of mixing in some rewrite rules, but some of the options you’ve explored seem pretty desperate and crazy instead of simply using a proxy rule. (like iframes?)
    Meh, it may not be a software solution, but the old tried and true way is still the best.

Comments are closed.