CORS: Cross-Origin Resource Sharing

security

#1

CORS or Curse?

Same-Origin Policy

In the beginning there was only SOP: one site could only run arbitrary code that came from itself, and you would trust them to do the right thing. If an attacker would try and retrieve your precious cookie, that would fail. SOP restricts third-party access to restricted content (e.g., your web-mail or your bank account’s credentials).

But what if we had an API for sharing commons resources, and we wanted those resources to be widely available, so that everyone could use our maps of the Commons, and create their own? We would need to find a way to authorize third-parties to access our API in a way that guarantees they’re the good people and prevent the bad guys from ruining our plans.

It’s not a secret that this Web is a minefield: SSRF, XSRF, XSS, etc. are all kinds of vulnerabilities that can take advantage of the complexity of Web services to impersonate you and make you do silly things. :scream:

image

CORS to the Rescue

Enter Cross-Origin Resource Sharing. The challenge when designing CORS was how to enable web applications to request and receive more cross-origin permissions, without exposing existing applications to new attacks. [] – CORS for Developers

INCOMMON::CORS

We need to define a strategy to ensure the maximum availability of IN COMMON services while protecting our resources from tampering, and our organizations from becoming vulnerable to new attacks via resource sharing. This strategy must be implemented through various complementary means.

Public Resources

Fortunately we’re mostly dealing with public resources. So we can simply allow GET requests from anywhere. CORS provides Access-Control-Allow-Origin: * for anonymous requests (truly public resources without any credentials). Phew, that was easy. But then, how can you create and update resources safely?

First you can use adm.incommon.cc which (will) provide a Web interface (using CORS) for Agents (collective resource managers) to create, update, and delete resources. As it runs on our domain with our code, and this code is free software, it’s a safe bet. But that doesn’t help with distributed resources and offline editing, two features that we want to see in the future.

Some resources, being public, can be accessed freely without problem: for example, Maps. Maps collect and present a selection of curated resources in a cartographic form. Nevertheless, there are cases where some resources presented on a specific Map may not be compliant with the IN COMMON Charter: then these resources should be available only through this particular Map, through the authenticated Agent in charge. CORS allows us to make such restrictions, allowing some projects to use non-compliant resources for their specific needs. But that’s another discussion. Let’s stick to CORS.

Maintaining Resources

The difficulty comes when we want anyone to be able to create new resources and join teams to maintain them, from neighbors to translators. By this we mean to use the INCOMMON::API to its full extent. The way we can do it in a safe manner is to insist on the collective freedoms of free software, that is the formation of a strong community and web of trust. Knowing each other is the best way we can ensure the service will be not only a technical endeavor but also, itself, a Common.

Of course, there are several technical approaches that will help us. I am going to share a few thoughts about this in a moment. But before that, a little explanation of the distributed resources and offline editing capabilities that I mentioned earlier.

Distributed Resource & Offline Editing

The most wanted feature for INCOMMON::API is to be able to add new resources. The purpose of IN COMMON from the beginning is to enable different organizations to share resources and use cartography to give their own perspective on the Commons.

As the demand for adding new points on the map has been growing faster than the code base, a wizard was created to make it easier to create a new resource right here and right now, on our community platform. Although this is not yet the Map, it provides a number of advantages. And once they’re registered here, they become available programmatically so they can be imported in the IN COMMON database as soon as possible: so it’s not wasted time! Go ahead and map it!

image

So, what’s the fuss about distributed resources and offline editing? – Bear with me, we’re still into CORS.

We have a common use-case for IN COMMON: working with the people on the ground, the conditions to gather data do not always coincide with the availability of a proper Internet connectivity. Besides, you are already working with existing data-sets and cannot wait for the API to come out. It’s about long term vision. IN COMMON is designed with this in mind, and allows for arbitrary data to enter the database. If you have a rough idea of the models, you can already prepare your data for it! That’s why all resources will have a UUID.

Universally Unique Identifiers

cat /proc/sys/kernel/random/uuid

Assigning a UUID to your records will allow you to keep track of them once they enter the IN COMMON database. That means your data can be created and maintained offline, on premises, and later synchronized with IN COMMON. Yes, that’s right. You don’t need IN COMMON to make IN COMMON. All we’re doing here is taking what exists and putting it into a form that can be reused anywhere, anytime.

Think about cartoparties: you go there, you map it, then you share it. No need to wait! It’s already there. Here’s a random UUID (this is what we’re using): 23d8009f-f2ec-44b6-bcbe-58ad1d3af10b. You don’t need to memorize it, just copy-paste it somewhere. This particular one, among the too many UUIDs available and still to generate has a special significance for us: it is the default public API key that can be used for read-only access to IN COMMON resources.

A sample of 3.26*10¹⁶ UUIDs has a 99.99% chance of not having any duplicates. Generating that many UUIDs, at a rate of one per second, would take a billion years. – Ludi Rehak

The probability of one duplicate would be about 50% if every person on earth owns 600 million UUIDs. […] Thus, anyone can create a UUID and use it to identify something with near certainty that the identifier does not duplicate one that has already been, or will be, created to identify something else. Information labeled with UUIDs by independent parties can therefore be later combined into a single database, or transmitted on the same channel, with a negligible probability of duplication. – WikiPedia article on UUID

All Right – Back to CORS

So, you’ve got your API key: 23d8009f-f2ec-44b6-bcbe-58ad1d3af10b.
Now you can run:

curl \
  -H 'Accept: application/vnd.api+json;version=0' \
  -H 'Authorization: Token 23d8009f-f2ec-44b6-bcbe-58ad1d3af10b' \
  -X GET \
  'https://api.incommon.cc/taxonomies/a001432e-53a9-452e-9966-e27271b910d6'

and get this JSONAPI snippet:

{
  "data": {
    "id": "1",
    "type": "taxonomies",
    "attributes": {
      "name": "Dewey Maps",
      "summary": "Belgique Mode d'Emploi (BMDE) is the original map that triggered the creation of IN COMMON.",
      "description": "# Belgique mode d'emploi\n\nThe [Dewey Maps](https://maps.dewey.be/) taxonomy is oriented towards shared commons resources from a sociological standpoint. It provides references to all kinds of public, commons, and shared resources to help citizens and exiled people survive and collaborate better.\n\nThis is the reference taxonomy for IN COMMON.\n",
      "uuid": "a001432e-53a9-452e-9966-e27271b910d6",
      "created_at":"2018-10-10T18:05:03.980Z",
      "updated_at":"2018-10-10T18:05:03.980Z"
    },
    "relationships": {
      "categories": {
        "meta": {
          "included": false
        }
      },
      "sections": {
        "meta": {
          "included":false
        }
      }
    }
  },
  "jsonapi": {
    "version": "1.0"
  }
}

OK that was a contrived example. In fact, for most resources you would just run:

curl https://api.incommon.cc/taxonomies/a001432e-53a9-452e-9966-e27271b910d6

But for some resources, an anonymous connection won’t suffice: here’s where CORS comes into play.

Backend to Keep CORS a Blessing

Thanks to Pundit and Rack::CORS, we can now filter access to any resource based on where it’s coming from. So, if someone uploads resources that are deemed out of line of IN COMMON aims to identify, promote, and defend the Commons, we can easily restrict their access to the Agents who have a use for them, while protecting the masses from exposure to their <choose-a-color>washing! That’s one way to focus on our forces, not on our problems.

Let’s say a large advertising company who invested a lot in IoT and trades public space for advertising space – that’s invasive advertising space (a pleonasm) since it comes with movement and personal information tracking in its vicinity – came up with the fantastic idea of providing a bike rentals service. Whoa! Ain’t that cool? That’s a shared resource proper, eh? Well, according to our Charter, not. It’s a rather brutal way of turning public space into a mall and surveillance space. Not at all what we mean by the term Commons. So if you try to retrieve those resources anonymously, you won’t find anything at all: that’s CORS in action, for the good.

But is it enough? Certainly not. COBAs are in the wild. Cross Origin Bypass Attacks abuse “improperly configured CORS”. But that’s not all: in the link above, you can see that a properly configured CORS can also be abused if you don’t take more precautions, i.e., when you don’t take extra care checking the origin and granting additional permissions properly.

image

We are still far from being an attack-worthy service – let’s start with being a service – but that illustrates the complexity of designing Web services for the long play… Watch this space :bookmark: for updates…