Cracking and Fixing REST APIs

REpresentational State Transfer, or REST, is more of a force on the web than most think. It is essentially a Web Service implementation of the HTTP protocol that runs the entire world wide web. I’m not hear to talk about REST, though, others have done that better than me.

I’m hear to talk about breaking REST.

When pentesting, I see the same pattern over and over. Organizations that went with a Service Oriented Architecture in the 2005 time frame had all of their business logic available as services in 2010 when the mobile boom hit. To make sure the IPhone app had the same functionality as the web app, they pushed the services through the DMZ without sufficient testing.

In this post, I’ll cover some of the common vulnerabilities that I find in REST APIs, and how to fix them. There are three main messages I want to get across: REST can be attacked like the rest of the web, REST can be attacked in special ways, and REST has special architectural considerations.

REST Can be attacked like the rest of the web

A REST API isn’t much different from a website. You start with a URL:

https://www.googleapis.com/language/translate/v2?key=sfo37ehf3olvmo8&source=en&target=de&q=Hello%20world


And then you get some markup back. Unlike normal web sites, however, we get JSON back rather than HTML.

{
    "data": {
        "translations": [
            {
                "translatedText": "Hallo Welt"
            }
        ]
    }
}

This means that we can use all of the attack vectors that one would use on a normal website. They might look a little different, but they end with essentially the same result.

Injection

SQL Injection and Cross Site Scripting (Browser injection) are possible because the parameters of a REST API call are what we would usually think of as directories in a normal web request. As long as we remember to check,

Parameters themselves can be tested too, if they are used by the API. You never know how they might be used.

https://www.googleapis.com/language/translate/v2?key=sfo37ehf3vmo8&source=en
&target=de&q=Hello%20world%3Cimg%20src%3D%27%23%27%20onerror%3Dalert(1)%20%2F%3E

 

How can this be fixed? On the SQL side, parameterized queried, as usual. XSS is a little tougher, but really it is just using the same techniques that one would use for a usual website.

Information Disclosure

REST responses use the same header format as regular web browser responses. Leaving unneeded information in those headers leads to information leakage:

HTTP/1.1 200 OK
Date: Thu, 07 Aug 2014 17:09:34 GMT
Server: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.7l DAV/2 PHP/5.2.15
Last-Modified: Mon, 07 May 2012 17:58:32 GMT
ETag: "1a273c-37e-4bf7604a7e200"
Accept-Ranges: bytes
Content-Length: 894
MS-Author-Via: DAV
Keep-Alive: timeout=15, max=500
Connection: Keep-Alive

The same is true of error messages. REST services should use HTTP response codes, and avoid pushing default web server errors to the clients, like this:

image

Authentication

Authentication in REST is a bit of a pain. When there is No Human Involved, you can’t just use a username and password in a SSL protected POST. There has to be something at play that is automated.

One thing you don’t want to do is put the secret key right in the URL. Even under SSL this is a bad idea.

image

 

 

What you do want to do is look into HMAC.

image

 

 

 

 

 

 

HMAC, or Hashed Message Authentication Code, is a process where the client and server both know a public key and a secret key. The client creates a request, concatenates it with a secret key, and hashes it. Then the request (without the secret key of course) is sent, along with the hash. The server will them accept the request, use the public key to look up that secret hey, concatenate the request and key and hash.

Session Management

Session Management is hard in web development, but the server just has to know a little about you to give you a smooth browsing experience. This isn’t true for REST API calls. There is simply no reason to keep a session alive.

image

 

 

 

 

 

Just authenticate every time. It will save you so many headaches.

REST Can Be Attacked In Special Ways

Bad enough that all of the usual tricks work with REST, but there are special attacks and weaknesses.

Management of Secrets

The API key that acts effectively as a password needs to be protected, and this is very hard when you are running a JavaScript only application. For instance, in a Windows 8 app or an Angular.JS site, you might just end up leaving your keys hanging out in the open:

twitterTimeout = 20000;
var twitterClientSecret = "kXFKUW9t2spHa3zgJtYX77aaRKfT1swvF9yfFC2tX34";
var twitterConsumerKey = "3NgwT8Xc0BcHJtH60h4cvw";
var twitterAppsUrl = "https://twitter.com/settings/applications";
(function () {
    "use strict";
    var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;

    WinJS.Namespace.define("Twitter", {
        Service: WinJS.Class.define(function Twitter_ctor() {

So what are we going to do? We need to exchange the secret key for a single session token on the server, then write it to the JavaScript. Facebook does this very wellFacebook does this very well. When the client app requests the login page the server generates a unique token based on information sent in the request. The information used is always something the server knows, something the client knows, and something both know. So for example the server can generate a unique key based on User agent + current time + secret key. The server generates a hash based on this information and then stores a cookie containing only the hash on the client machine.

image

CSRF

CSRF is a big topic that is handled very well by OWASP. I’ll let them explain it if you aren’t familiar, but you need to know that if your REST API depends on the site’s session cookies, it is completely and totally susceptible to CSRF. Just don’t use state on your REST APIs.

Unused HTTP Verbs

REST uses the HTTP Verbs, usually GET, PUT, POST and DELETE as the action words in your APIs domain language. Trouble is, there are a lot of HTTP verbs and we probably aren’t going to use them all. That said, if you had to open up PUT and DELETE on your server, you might have opened up others (like, all of them.)

Once others are opened, if they aren’t specifically in your authorization configuration, then we can use a verb like HEAD and bypass authentication and perhaps get a token:

telnet www.example.com 80
HEAD /admin/pageIwannaSee.aspx HTTP/1.1

So what do we do? Turn off the unused verbs, or include them in your configuration.

Direct Object Reference

Direct object reference is not specific to REST but it is particularly relevant to REST. For instance, take a look at a call to the Facebook API at https://graph.facebook.com/v1.0/1138975845:

{
  "id": "1138975845", 
  "first_name": "Mary", 
  "gender": "female", 
  "last_name": "Loaiza", 
  "link": "https://www.facebook.com/mary.loaiza.921", 
  "locale": "es_LA", 
  "name": "Mary Loaiza", 
  "updated_time": "2014-06-08T01:32:22+0000", 
  "username": "mary.loaiza.921"
}

And then check out the next integer, https://graph.facebook.com/v1.0/1138975846

{
  "id": "1138975846", 
  "first_name": "Chase", 
  "gender": "male", 
  "last_name": "Krywaruchka", 
  "link": "https://www.facebook.com/chase.krywaruchka", 
  "locale": "en_US", 
  "name": "Chase Krywaruchka", 
  "updated_time": "2013-09-17T03:17:00+0000", 
  "username": "chase.krywaruchka"
}

With parameters in the URL at this level, you need to be especially careful to use unique identifiers, like GUIDs perhaps, for your object references. Otherwise you run the risk of someone downloading your entire user list – not to give you any ideas.

Mass Assignment Vulnerability

The Mass Assignment Vulnerability is a special flaw in ActiveRecord, a database access methodology commonly used in REST APIs. For instance, take this Person object, right from Uncle Bob:

image

 

 

 

 

It is possible, in many languages, to instantiate a new Person in such a way that it just sucks in all of the correctly named form fields. However, a malicious user can change the client side JavaScript to set other fields that might not be on the form:

params[:person] = { isFlaggedForAudit: true}


This is because ActiveRecord will autogenerate the underlying class on the API side, and it won’t distinguish between fields that the user should be able to set than those the user shouldn’t have write access to. For publically accessible classes, it’s recommended that you explicitly exclude sensitive fields, as shown.

[Bind(Exclude = “IsFlaggedForAudit")]
public class User
{
  public int FirstName{ get; set; }
  public string LastName{ get; set; }
  public string NumberOfDependents{ get; set; }
  public bool IsFlaggedForAudit{ get; set; }
}

REST has special Architectural Considerations

Aside from the general good practices and specific code protection, there are a few overriding considerations that should go into planning an API.

Carefully Consider Your Authentication

Consider the audience for your API and then plan for authentication early. If you have a small number of discrete users, consider using digital certificates for authentication. They are a pain to set up, but it doesn’t get more secure. If you have a public audience, then look into HMAC. It is supported on most platforms.

Treat your API Keys like PKI

If you go with HMAC, make sure your user base understands to treat their secret key like a private key in a PKI environment. It is literally the key to the kingdom. I can’t even begin to tell you haw often I have found the secret key in the URL (not secret) or the comments of a JS file (also not secret).

Treat Your URL Like A Method

In REST, the URL is your method signature. Plan them that way. Name them intelligently, and make sure they aren’t leaking information that shouldn’t be in there.

image

 

 

 

 

 

 

Burp even has a special tool for fuzzing REST style parameters. Just because we don’t have the parameter names doesn’t mean they can’t be fuzzed.

Treat Your API Like A Web Site

Finally, don’t assume that because your web site was tested, that you can just go and expose previously internal services as external REST APIs. The API needs to be tested and reviewed separately. Treat it like a site of its own.

Building a tool for REST testing

I am working on a BURP plugin for checking for several of these. You can find the project on Google Code. If you’d like to be in on the project, please let me know and you can take a piece and work on it.

Comments are closed

Bill Sempf

Husband. Father. Pentester. Secure software composer. Brewer. Lockpicker. Ninja. Insurrectionist. Lumberjack. All words that have been used to describe me recently. I help people write more secure software.

profile for Bill Sempf on Stack Exchange, a network of free, community-driven Q&A sites

MonthList