Aston J

Rails mass assignment security

Posted on: March 6th, 2012 by AstonJ 5 Comments

While Rails does a lot to secure your app, some things it leaves to you (as one size doesn’t fit all) and mass assignment security (MAS) is one such example. Rails does of course, make it easy for you to bolt things up, here’s how.

When will you need MAS?

Whenever you are accepting data from users, such as from a form via a params[:hash] and using update_attributes (and family) eg:

  def create
    @post = Post.new(params[:post])
    if @post.save
      # do something
    else
      # do something else
    end
  end

Why do you need MAS?

Because a hacker can easily get their browser (or use other tools such as curl) to send whatever parameters they want to your site mimicking your form – so you need to tell Rails which attributes should be ‘accessible’. If you don’t, and say your User model/table has a boolean field for admin, then someone could just set themselves as admin.

How do you switch it on?

Easy, in config/application.rb, just uncomment this line:

config.active_record.whitelist_attributes = true

Then at the top of each model, set which attributes you want to allow to be accessible:

attr_accessible :email, :password, :password_confirmation, :remember_me, :name

Then to safely update a protected attribute, you simply set the attribute separately:

 @user.admin = true

Are there any alternatives?

Yes, here’s @DHH’s way of dealing with it:

class PostsController < ActionController::Base
  def create
    Post.create(post_params)
  end
 
  def update
    Post.find(params[:id]).update_attributes!(post_params)
  end

  private
    def post_params
      params[:post].slice(:title, :content)
    end
end

Which then allows you to allow/disallow what is accessible by the user depending on who they are:

def post_params
  if current_user.admin?
    params[:post].slice(:title, :content, :published)
  else
    params[:post].slice(:title, :content)
  end
end

But you only really need that if you have different sets of permissions per user – although I think it’s good practise to get into.

Further reading

The Rails guides on security: Rails Guides / Security

Tags: ,
  • http://excid3.com Chris Oliver

    I think it’s really important to realize that making attr_accesible like you’ve suggested is still potentially insecure.

    Say you’ve got a User that has an admin boolean. You might be tempted to attr_accessible that field too when you’re doing that. It makes it easy for you as the developer to have a hidden field for admin when you’re creating new users. When you give the ability for the user to create admins and regular users, you’re going to want to not attr_accessible the :admin property, but instead post them to different actions that explicitly set user.admin = true or false.

    • AstonJ

      Hi Chris I totally agree – I wouldn’t make anything attr_accessible unless it’s something I want to let *any* user update, OR, use it in conjunction with DHH’s method.

      (Do you think I need to edit my post to emphasize that? I didn’t think my post sounded like doing that was ok :/)

      • http://excid3.com Chris Oliver

        After your attr_accessible line, you should show an example of how to safely update an attribute like you do with DHH’s method. :)

        • AstonJ

          Thanks, done! Reckon that will suffice?

          • http://excid3.com Chris Oliver

            Yes that’s perfect. Thank you sir! :D