How to check for access control vulnerabilities in Rails applications

By Najaf Ali

By far the most common security vulnerabilities I see in Rails applications
revolve around access control (a.k.a. authorization). Here's how to quickly scan
your codebase for the most obvious forms of these vulnerabilities.

Access control is the part of the code that answers the question Is this user
allowed to do what he's trying to do? It's common to find Rails apps where
this code is either broken or non-existant.

Quickly grep the codebase for a smoking gun

The offending code will most likely be updating a model without checking if the
user should be allowed to. So a quick way to search for that would be:

This should give you a rough idea of where models are getting created, modified
or deleted in your controllers. Most Rails apps will have a few entries for each
of the method calls listed in the above regex.

Check access control around each method call

For each of these method calls, (we'll call them 'updates') ask yourself the following:

Should all users be allowed to update the model in this way?

If not, can you point out the code that stops users who shouldn't be allowed to
do the update?

Should users be allowed to set all the fields on the model in question?

If not, can you point out the code that stops users from updating all
disallowed fields?

If you can't point out the code that enforces your applications access control
rules, your code is vulnerable. Here are some things an attacker
may be able to do if you have this sort of vulnerabilty in your application:

View other users data

Upgrade priveleges to an 'admin' on his own or another users account

Delete entire tables worth of data from your application

That's just a small sample.

Most applications suffer from this class of vulnerability in some form or
another. While fixing access control won't make your web application
bullet-proof, it's one of the low-hanging fruit that security researchers will
try first when attacking your application.

That we have a current_user, probably provided by devise or a library like it.

That current_user.bananas gives us an ActiveRelation that scopes banas to current_user. When
we call current_user.bananas.find, we're searching within the current users bananas.

For this code to work:

current_user can't be nil (a user must be logged in)

The banana that the user is trying to create, read, update or delete 'belongs to' the logged in user.

If either of those two criteria aren't met, the code will throw an error. This
is exactly what we want it to do as it means someone is trying to modify a
banana they're not allowed to.

This effectively implements access control for us. While there is still the
potential for security vulnerabilties in this code (you'll notice that the
parameters haven't been whitelisted) this covers off the
absolute basic requirements for access control around a resource.