Ruby on Rails: Protect params from injection

By | December 5, 2007

Using the ActionControllers params method directly within your models can be very risky. You know doing this: User.new(params[:user]). Someone could easily create his own form adding additional parameters and therefore updating your model without your knowledge. The reason is clear: you take all available attributes with params. This article shows some solutions to protect your forms against such injections.

I want to show three solutions which follow different approaches:

1. Use attribute accessors to protect your attributes

You can mark your attributes with attr_accessible or with attr_protected. Marking with attr_protected excludes them from mass assignment. They can ONLY be changed through direct assignment.

  1. class User
  2.   atrr_protected :admin
  3. end
  4.  
  5. #in your controller
  6. #mass assignment: admin would never be assigned
  7. User.new(params[:user])
  8.  
  9. #needs to be done manually
  10. User.new(params[:user]).admin = params[:user][:admin]

Whereas the attr_protected approach is some kind of Blacklist, using attr_accessible results in a whitelist. Mark all your attributes with attr_accessible which are allowed for mass assignment.

  1. class User
  2.   attr_accessible :firstname, :lastname
  3. end

The whitelist has the advantage that you are on the safe side if you add sensitive columns to your model in the future. They will be ‘automatically’ protected against injection.

2. Extend the hash object to allow only specified keys

The params method returns a hash, so it would be a solution to extend the hash and specify the attributes we want to use. This keeps all other existing parameters out of our model. So we are happy :)

  1. class Hash
  2.   def keep_keys(keyList)
  3.     self.each_key { |key| delete(key) unless keyList.member? key }
  4.   end
  5. end

(The snippet above should live in /lib and be loaded within your environment.rb with require – taken from wiki.rubyonrails.org)

Now a mass asignment looks like the following

  1. params[:user].keep_keys([:firstname, :lastname])

3. Add a method to the ApplicationController which only extracts the needed attributes

The last solution within this post outlines a method which does all the protection stuff for us. We call it (lets name it attributes_from_params) within our controllers and tell it which attributes we need from the params. Place this into your ApplicationController:

class ApplicationController < ActionController::Base protected def attributes_from_params(object, *attrs) raise ArgumentError, 'must specify at least one attribute' if attrs.size == 0 attributes = {} return attributes unless params[object] attrs.each { |a| attributes[a] = params[object][a] } attributes end end [/ruby] The call within your controller could look the following: [ruby] u = User.new(attributes_from_params(:user, :firstname, :lastname)) [/ruby] Summary

You should never use mass assignment for your models from a controllers parameters. It is very easy for an attacker to save the form and extend it with additional parameters. E.g. let’s say you have an admin attribute which indicates if a user is an admin or not.
The article described three solutions for this problem. You can protect your attributes with attribute accessors attr_protected (which acts as a blacklist) or with attr_accessible (which acts as whitelist). This approach excludes attributes from mass assignment. The problem here is that you cannot fully use the mass assignment anymore and you don’t cleary see which attributes are assigned during the mass assignment and which not.
Personally I prefer an approach which allows me to see which attributes I am assigning during mass assignment. This is reflected in the 2nd and 3rd approach. With both approaches it’s clear for everyone which attributes are taken from the parameters and I can still use the power of mass assignment (e.g. admin attributes in an admin section).
Last but not least the 3rd approach is nice to read because it says ‘give me those attributes from the parameters’. At the end it’s up to you to choose the best solution for your needs, but don’t forget to choose one for the protection of your forms ;)

More reading on this problem:
Creating records directly from form parameters
How To Prevent Form Injection
Ruby on Rails Security Guide

Leave a Reply

*

* Copy This Password *

* Type Or Paste Password Here *

147,888 Spam Comments Blocked so far by Spam Free Wordpress