Wednesday, September 3, 2008

Securing REST API's against "Drive By" Requests

REST protocols are increasingly popular to enable API calls to be sent to web services. These requests can be generated from one server to another, or they can be embeded in client-side web pages. But client-side only requests are inherently unsafe; anyone that can navigate to that page, can have REST calls made against a 3rd party service without their knowledge.

I couldn't find a brief summary of common practices for securing REST API's, so I did some investigation. I reviewed the del.ico.us api to see how they deal with these problems. They have a REST api that, among other things can add and remove bookmarks for a given user. So there needs to be some security so that a 3rd party does not execute this API on behalf of a given user without their permission.

For example, executing this "API" call:

https://api.del.icio.us/v1/posts/add?url=http://mckoss.com&description=security+test
will cause a bookmark to be created in your del.icio.us account. This could even be hidden in a web page as an emdeded <script> tag, so that you would not even be aware that it is happening. Note that POST's are easy to create in the client as well as GET's, so there is not really any added security to using POST over GET.

In order to prevent this, del.icio.us implements two countermeasures:

  1. All calls to their API that modify data require Basic Authentication.
  2. If there is a Referer value in the request header, they return a 403 Forbidden error.
Note that, even if I am logged in to del.icio.us, I will have a authentication cookie on my machine. But this is not sufficient to authenticate to the API - since it uses a different form or authentication. It is possible, however, that a user has previously logged into the del.icio.us API and cached their credentials in the browser. So this protection is a speed-bump, but not fail safe protection.

The second requirement protects users from "drive-by" API calls - the fact that I am visiting a web page should not allow that page to issue requests on my behalf. All mainstream browsers will send a Referer header whenever they make a request from another web page.

One work-around is to download an html page to your local machine, and execute it from your file system. In this case, there is no Referer and the API request is executed. So, a malicious site would have to trick a user into downloading a page and opening the local copy - which is much harder to do. There is a more robust alternative to these measures, that more modern REST servers implement. That is, to require that some user-specific secret information be transmitted along with every request. That way, a random malicious web page will have no way of generating an API call for an unknown potential victim.

The FriendFeed API accomplishes this simply by generating a random RemoteKey for each user. Any service that wishes to make API calls on behalf of the user must include the current value of that user's RemoteKey. In case of a security breach, the user can reset the key (and give the new key to all the services he wishes to continue to use).

It's also not uncommon for web application frameworks to generate hidden session keys within a form that is unique to the session. That way they eliminate the ability for pages to cross-post forms from malicious web sites.

4 comments:

  1. Jaiku implements the same API key idea as FriendFeed does.

    ReplyDelete
  2. The official security term for this vulnerability is "Cross Site Request-Forgery". Damon pointed out similar known bugs:

    http://laconi.ca/trac/ticket/503
    http://identi.ca/tag/503

    ReplyDelete
  3. This is most definitely pretty common and as rapid web application development and RESTful frameworks become more popular, I see this as a fairly common occurrence among web sites.

    While many devs have been alerted to the fact that GET URLs that modify data (i.e. /messages/delete/1234) are bad (and violates RFC to boot) due to the prevalence of web crawlers, some still have yet to realize that POST forms are nearly as vulnerable.

    It's even more of a concern in social networking sites as URL shortening services are the norm, and easily obscure a dangerous web page. Not only that, but you could go to a perfectly normal looking web page and if you're not using a javascript blocker like NoScript, it can execute commands on your behalf on any site that does not use some sort of token in their forms, or other means of protection.

    Unfortunately, many devs don't quite understand the implications of this and it requires a proof of concept (as in the laconi.ca case) to actually illustrate the damage that can be done. In the laconi.ca example - I had that bug filed for 2 weeks, but once somebody demonstrated the exploit, it was fixed within a matter of hours.

    ReplyDelete
  4. CSRF (Sea-Surf) is another reason I don't use online banking. When a bank official asks, I say "no thank you". What I really would like to say is "What measures do you take to prevent XSS and CSRF?"

    ReplyDelete