3

I tend to not need the mass-assignment feature in my production code. (In my test code, I use it a lot, but in those cases I do want to set arbitrary columns.)

So if, in my production code, I simply avoid these forms:

Article.new(params[:article])  # or create
article.attributes = params[:article]
article.update_attributes(params[:article])

and instead always manually enumerate all the attributes, like so:

Article.new(:title => params[:article][:title], :body => params[:article][:body], ...)

am I save from mass assignment security issues (even without using attr_accessible/attr_protected)?

Edit: The reason I'm not just disabling mass assignment is, I'd like to be able to write Article.create!(:blog_id => @blog.id, ...), where blog_id is an "unsave" attribute.

Jo Liss
  • 30,333
  • 19
  • 121
  • 170

3 Answers3

10

Yes, using the 2nd method, you're safe from users assigning to other attributes.

This is a DRYer way to write it, though:

Article.new(params[:article].slice(:title, :body))

-or-

def article_params
  params[:article].slice(:title, :body)
end

Article.new(article_params)  # or create
article.attributes = article_params
article.update_attributes(article_params)
John Douthat
  • 40,711
  • 10
  • 69
  • 66
2

Add this at the end of config/environments/production.rb :

ActiveRecord::Base.send(:attr_accessible, nil)
Zabba
  • 64,285
  • 47
  • 179
  • 207
  • But I don't want to do this, because I'd like to be able to write `User.create!(:name => 'J. R. Hacker', :admin => true)` in my test code. My question is, if I don't globally disable mass assignment, am I still save? – Jo Liss Mar 24 '11 at 20:04
  • 2
    The above will only apply to your production code, not your test code – Zabba Mar 24 '11 at 20:05
  • Ah, right, of course. Here's the problem though: If I do this, I have to write `a = Article.new; a.blog_id = blog.id; ...; a.save!`. I'd much rather like to write `Article.create!(blog_id => blog.id, ...)` -- but I can only do that if I leave mass assignment open, even on unsafe attributes like blog_id. So I'm wondering if that's a save thing to do at all (so long as I avoid passing in tainted hashes). – Jo Liss Mar 24 '11 at 20:11
  • You are right, you will need to assign to each attribute in this case. You could use `attr_protected`, all attributes will be mass-assignable except those marked as protected. – Zabba Mar 24 '11 at 20:24
0

I could not get John Douthat's method working for multiple parameters, so I came up with the following alternative (taken from my CommentsController):

def set_params
  @comment.parent_id = params[:blog_comment][:parent_id] 
  @comment.ip_address = request.remote_ip
  @comment.commentator = current_user.username || "anonymous"
  @comment.name = params[:blog_comment][:name]
  @comment.email = params[:blog_comment][:email]
  @comment.url = params[:blog_comment][:url]
end

def create
  @comment = @post.comments.build(params[:blog_comment])
  set_params

  if @comment.save
  ...
end

def update
  @comment = Blog::Comment.find(params[:id])
  set_params

  if @comment.update_attributes(params[:blog_comment])
  ...
end
Blu Dragon
  • 394
  • 5
  • 14