Rails - Using Whitelists for Mass-Assignment Security
For 3.2 upgrade, and to improve protection against mass-assignment exploits, we recently changed an app to use a more strict "whitelist" approach. In case you missed it, there was a lot of drama recently around the exploit: http://www.infoq.com/news/2012/03/GitHub-Compromised. Basically, the power of Rails can be used to sneak in extra attributes (that are not presented in the UI) into requests, attributes that map to database properties. This Rails magic happens with any method that ultimately calls ActiveRecord update_attributes.
One way to tighten this up is by setting the following in application.rb:
config.active_record.whitelist_attributes = true
With that set, only attributes that are explicitly defined by attr_accessible are eligible for update from update_attributes. This includes nested attributes that correspond to accepts_nested_attributes_for, for example :line_item_attributes.
attr_accessible :status, :line_items_attributes
This is great for models that only have one form or interface for mutation, but often models are used in various use-cases: End user UI, administration UI, scripts, etc.
Whitelisting for multiple contexts
Rails provides a very simple and flexible approach to support the multiple contexts, the :as parameter or so-called "role." In the following, we specify a separate role or context, which in effect creates a separate whitelist.
attr_accessible :status, :line_items_attributes, :item_total :as => :admin
...this whitelist would then be applied by also setting the :as option in the mass-assigning call...
order.update_attributes(order_attributes_including_item_total, :as => :admin)
This works when one has controll over the AR calling code, and there is also a way of applying the role for all calls.
Applying a whitelist role to an entire controller
Using a role or :as context does the trick, but it can be tedious if every action in the controller uses the same context. And there are cases where the developer does not have access to the method that results in the update_attributes call, such as controllers that use inherit_resources for automagic Rails wiring. In these cases the developer can call the with_role class method to set the role for all actions in the controller.
class Admin::OrderController < ApplicationController inherit_resources with_role :admin
Using whitelists, especially combined with roles, is a pretty powerful tool and I think that it should obviate explicit management of attributes in controller logic. I hope this post proves useful to you, Internet.