views:

3383

answers:

3

I'm getting started on building a REST API for a project I'm working on, and it led me to do a little research as to the best way to build an API using RoR. I find out pretty quickly that by default, models are open to the world and can be called via URL by simply putting a ".xml" at the end of the URL and passing appropriate parameters.

So then the next question came. How do I secure my app to prevent unauthorized changes? In doing some research I found a couple articles talking about attr_accessible and attr_protected and how they can be used. The particular URL I found talking about these was posted back in May of '07 (here).

As with all things ruby, I'm sure that things have evolved since then. So my question is, is this still the best way to secure a REST API within RoR?

If not what do you suggest in either a "new project" or an "existing project"scenario?

+2  A: 

I'm facing similar questions as you at the moment because i'm also building out a REST api for a rails application.

I suggest making sure that only attributes that can be user edited are marked with attr_accessible. This will set up a white list of attributes that can be assigned using update_attributes.

What I do is something like this:

   class Model < ActiveRecord::Base  
       attr_accessible nil  
   end

All my models inherit from that, so that they are forced to define attr_accessible for any fields they want to make mass assignable. Personally, I wish there was a way to enable this behaviour by default (there might be, and I don't know about it).

Just so you know someone can mass assign a property not only using the REST api but also using a regular form post.

jonnii
+5  A: 

How do I secure my app to prevent unauthorized changes?

attr_accessible and attr_protected are both useful for controlling the ability to perform mass-assignments on an ActiveRecord model. You definitely want to use attr_protected to prevent form injection attacks; see Use attr_protected or we will hack you.

Also, in order to prevent anyone from being able to access the controllers in your Rails app, you're almost certainly going to need some kind of user authentication system and put a before_filter in your controllers to ensure that you have an authorized user making the request before you allow the requested controller action to execute.

See the Ruby on Rails Security Guide (part of the Rails Documentation Project) for tons more helpful info.

Gabe Hollombe
+18  A: 

There are several schemes for authenticating API requests, and they're different than normal authentication provided by plugins like restful_authentication or acts_as_authenticated. Most importantly, clients will not be maintaining sessions, so there's no concept of a login.

HTTP Authentication

You can use basic HTTP authentication. For this, API clients will use a regular username and password and just put it in the URL like so:

http://myusername:[email protected]/

I believe that restful_authentication supports this out of the box, so you can ignore whether or not someone is using your app via the API or via a browser.

One downside here is that you're asking users to put their username and password in the clear in every request. By doing it over SSL, you can make this safe.

I don't think I've ever actually seen an API that uses this, though. It seems like a good idea to me, especially since it's supported out of the box by the current authentication schemes, so I don't know what the problem is.

API Key

Another easy way to enable API authentication is to use API keys. It's essentially a username for a remote service. When someone signs up to use your API, you give them an API key. This needs to be passed with each request.

The downside here is that if anyone gets someone else's API key, they can make requests as that user. I think that by making all your API requests use HTTPS (SSL), you can offset this risk somewhat.

API Key + Secret Key signing

Significantly more complex is signing the request with a secret key. This is what Amazon Web Services (S3, EC2, and such do). Essentially, you give the user 2 keys: their API key (ie. username) and their secret key (ie. password). The API key is transmitted with each request, but the secret key is not. Instead, it is used to sign each request, usually by adding another parameter.

IIRC, Amazon accomplishes this by taking all the parameters to the request, and ordering them by parameter name. Then, this string is hashed, using the user's secret key as the hash key. This new value is appended as a new parameter to the request prior to being sent. On Amazon's side, they do the same thing. They take all parameters (except the signature), order them, and hash using the secret key. If this matches the signature, they know the request is legitimate.

The downside here is complexity. Getting this scheme to work correctly is a pain, both for the API developer and the clients. Expect lots of support calls and angry emails from client developers who can't get things to work.

Micah
many people are going down the API key + signing path, anyone know if there is a 'better way'?!
MatthewFord
Nice answer - this finally cleared it up for me with the different levels. Thanks!
Brian Armstrong
There are two big problems with HTTP "Authorization:". The first is that the server decides on the auth scheme, which means that Digest authentication doesn't prevent password-compromise by a MITM (just pretend that you only support Basic). The second is that Basic authentication isn't limited to a path, so anyone on a shared webserver can get anyone else's password (a lot of people are switching to subdomains for this reason, along with cookies and generally more sane Apache config).
tc.