Pages

Thursday, January 2, 2014

Understanding attr_accessible – Secure Your Rails Application

What is the attr_accessible directive ?
So, when you open up your devise User model, and you notice a line similar to that :
attr_accessible :username, :email, :password
The first thing that is important to know about, if you don’t really, is that attr_accessible is simply an instance method, a function as most non-ruby programmers would name. This is actually a part of ActiveModel::MassAssignmentSecurity::ClassMethods, which already some something about its usage. It’s there to prevent mass assignment security attacks.
But what exactly is that ?
 In Ruby on Rails, a mass assignment is when you save more than one attributes of a model to your database. For instance :
current_user.save
current_user.update_attributes(...)
current_user.update_attribute(...)   <- THIS IS NOT A MASS ASSIGNMENT

Notice that update_attribute is not a mass assignment and attr_accessible does not get triggered upon its execution. However, if you execute save to store changes in a model, what Rails does is saving ALL attributes for that model. It does not just save the attribute that you  changed, it saves all attributes. This can be a big problem. Let’s see an example of a simple create action :
def create
  @user = User.new(params[:user])
  respond_to do |format|
    if @user.save
      format.html { redirect_to @user, notice: 'User was successfully created.' }
    else
      format.html { render action: "new" }
    end
  end
end
I removed some code(like json response) for simplicity and to better illustrate the point. Notice that the params hash gets form information as inputted by a user in our application and creates a new user, based on that input. Now, our form can just have a name field for the user to fill in, but that does not mean that a malicious user cannot pass more information to the model. For example, if our User model contained the fields ‘username, password, role’, we would never want a user to set their role as admin, just by registering to our application.
This is a scenario that can happen here. It’s pretty trivial for an attacker to craft a malicious http query(either using curl, or tamperdata firefox plugin, choices are endless), in order to pass a user[:role]=’admin’ attribute. When @user.save gets executed, the attribute role is also saved and the attacker becomes an administrator.
attr_accessible to the rescue
What attr_accessible does is preventing this scenario. By specifying :
attr_accessible :username, :password
what you are doing is actually whitelisting all those model attributes that can be changed via a mass assignment operator. You are effectively saying to Rails : “When i execute user.save, i only want to save the username and password attributes. Do not save anything other than that”. Therefore, if the attacker tries to exploit the role field using mass assignment, Rails will just ignore the setter and just set all the other values, as listed in attr_accessible.
There is also an attr_protected method, which is pretty much the negation of attr_accessible. It specified which fields you want to protect. You can there specify :
attr_protected :role
and protect the role attribute for any mass assignments. However, i highly discourage you from using attr_protected, because it’s very easy to forget to blacklist an attribute. It’s safer to use attr_accessible and deny all assignments by default, while whitelisting the secure attributes. Firewall policies creators should acknowledge that in like 90% of cases. So, from now on, never forget to use attr_accessible in ALL your models. It’s not just a good practice, it’s an essential one.

No comments:

Post a Comment