GET doesn’t change the world
A Mendeley HTTP bug could trick visitors into adding papers unknowingly.
Recently, I wanted to offer my visitors the option to add any of my publications to their Mendeley paper library. When creating the “add to Mendeley” links, I noticed that papers got added without asking the visitor for a confirmation. Then I wondered: could I exploit this to trick people into adding something to their Mendeley library without their consent? Turns out I could, and here is why: Mendeley did not honor the safeness property of the HTTP GET
method.
Safeness and idempotence provide guarantees
The HTTP specification defines for each HTTP method whether it satisfies these two important properties:
- safeness
- A method is safe if its invocation does not change resource state.
- idempotence
- A method is idempotent if multiple invocations result in the same as a single one.
For example, the GET
method is defined as safe and idempotent: if you GET
a page about a recent movie, you expect your browser to show information about the movie’s cast and plot, not to buy you a ticket (yet). And if you GET
this page twice, you expect to see the same result again. This is unlike the POST
method, which is unsafe and non-idempotent: performing a POST
might actually buy a ticket. If you do it twice, you’ll have two tickets. Do it three times, and you’ll have tree tickets. Keep on repeating, and you’ll have the cinema hall all for yourself. So GET
is safe, POST
is not.
The correct implementation of the safeness and idempotence properties are essential to make HTTP clients (such as browsers) and devices (such as servers and caches) do their job. For example, a browser doesn’t warn you if you go through your history and want to reperform the GET
request on the movie’s page. This behavior is justified because there’s no harm GET
can possibly do: GET
does not change resource state (safeness), and repeating a GET
multiple times does not have a different effect than only issuing it once (idempotence). However, your browser will warn you if you try to reperform the POST
request, since POST
can do harm by changing the resource state and having a different result when executing the request multiple times (its unsafeness and non-idempotence might buy many tickets instead of just one).
Taking safe methods to a dinner
The safeness property is often explained in terms of “changing the world”. A GET
request shouldn’t change the world, but a POST
request can. However, what do we do for example with visitor statistics? Tools such as Google Analytics track when people visit pages, and most of these visits are GET
requests. Other sites such as YouTube count how many people watched a video, which is also a GET
operation. Or maybe the server performs actions internally as a result of GET
request, such as caching and optimization. Are these cases violations of the HTTP protocol?
Strictly speaking, they are, but the server must not consider any safe action as a deliberate advance by the client. In other words, performing safe requests is like browsing the menu at a restaurant. Sure, the waiter can make suggestions if he notices you’re into rib eye steak, but he can’t pass the order to the kitchen just yet. That’s an obvious matter of politeness, and an implicit agreement—
Mendeley wasn’t playing safe
When this agreement between client and server is broken, undesired behavior might occur. Mendeley, an academic social network and digital paper library, had an interesting security bug as a result of not honoring the safeness property of GET
. The URLs to add papers to Mendeley are constructed like this:
http://www.mendeley.com/import/?url=paper_url
For example, to cite a paper from my website, this becomes:
http://www.mendeley.com/import/?url=https%3A%2F%2Fruben.verborgh.org%2Fpublications%2Fverborgh_wsrest_2012%2F
What I noticed is that, when I clicked the link above—GET
request to that URL for me—
What’s worse is that I (or anybody else) could trick people that way into adding papers to Mendeley, without their consent. We just have to trick their browsers into performing a GET
request, which is actually easy. For example, we could add images to our homepage that look like this:
<img href="http://www.mendeley.com/import/?url=paper_url">
See what I did there? Here’s what would happen:
- A non-suspecting visitor decides she likes to visit our homepage.
- Her browser reads the
img
tag. - Her browser
GET
s the “image” (which is actually a page). - The image doesn’t display (since it’s a page), but…
- As a result of the
GET
request, Mendeley adds the paper to her collection!
Implementing a state-changing action as a GET
operation is not just a formal mistake, it can have disturbing, undesired consequences for end users.