Mass assignment是rails中常用的将表单数据存储起来的一种方式。不幸的是,它的简洁性成了黑客攻击的目标。下面将解释为什么及如何解决。
上述表单为一个简单的注册表单。当用户填入name,点击提交时,一个新用户被创建。用户模型被如下定义:
ruby create_table :users do |t| t.string :name t.boolean :admin, :default => false, :null => false end
当用户点击提交时,如下的action被执行:
ruby def create @user = User.new(params[:user]) if @user.save flash[:notice] = "Successfully registered" redirect_to :action => ’show’, :id => @user.id else render :action => :new end end
用户通过传入的params被创建,如前面讨论的,params中的内容不能被充分的信任。下面将展示hacker如何将自己注册为admin的。
攻击网站
如下的curl命令行用于向页面post参数.
terminal
curl -d "user[name]=hacker&user[admin]=1" http://localhost:3000/Users/
上面命令,发送了名字为‘hacker’并为admin的命令。命令行中查看如下:
terminal
Processing UsersController#create (for 127.0.0.1 at 2009-02-03 20:18:54) [POST] Session ID: 8daeaad6eb382c903e595e704b626ef7 Parameters: {"user"=>{"name"=>"hacker", "admin"=>"1"}, "action"=>"create", "controller"=>"users"} SQL (0.000390) INSERT INTO users ("name", "admin") VALUES(’hacker’, ’t’) Redirected to http://localhost:3000/users/show/2
黑客可以如此做,主要是因为mass assignment:我们使用params参数创建一个user,同时黑客传递的参数是符合的。
保护属性
预防这种攻击的方法是限制来自表单的数据被如此存储。Rails有attr_protected
方法定义method的属性不能通过mass assignment被设置。
ruby
class User < ActiveRecord::Base has_many :comments attr_protected :admin end
在 User model中,将admin属性设置为attr_protected
。
现在,如果再次发送上述的curl,会看到如下的log信息,即使params hash中提供admin为true,但admin属性没有被保存为true。
terminal Processing UsersController#create (for 127.0.0.1 at 2009-02-03 20:37:49) [POST] Session ID: 381cee077c1367bf0cc410a2259adb96 Parameters: {"user"=>{"name"=>"hacker", "admin"=>"1"}, "action"=>"create", "controller"=>"users"} SQL (0.000327) INSERT INTO users ("name", "admin") VALUES(’hacker’, ’f’) Redirected to http://localhost:3000/users/show/5
admin属性被设置成可false。
网站仍然存在漏洞。在应用中,很多模型之间存在关系,如1个user有很多comments,has_many提供了一种通过mass assignment设置comment_ids的方法。现在用如下的命令攻击comment ids.
ruby curl -d "user[name]=hacker&user[admin]=1&user[comment_ids][]=1&user[comment_ids]=2" http://localhost:3000/users/create
has_many
的关系使得user model具有comment_ids
=[],上述命令通过将commend_ids=[1,2]进行了攻击,如下的log展示了hacker用户拥有的comments。
terminal Processing UsersController#create (for 127.0.0.1 at 2009-02-04 20:27:36) [POST] Session ID: e6bee21260899c7dce47bc5040dcd467 Parameters: {"user"=>{"name"=>"hacker", "comment_ids"=>["1", "2"], "admin"=>"1"}, "action"=>"create", "controller"=>"users"} Comment Load (0.001) SELECT * FROM comments WHERE (comments."id" IN (1,2)) SQL (0.001) INSERT INTO users ("name", "admin") VALUES(’hacker’, ’f’) Comment Update (0.000094) UPDATE comments SET "title" = ’Comment 1’, "user_id" = 8 WHERE "id" = 1 Comment Update (0.000071) UPDATE comments SET "title" = ’Comment 2’, "user_id" = 8 WHERE "id" = 2
为了解决上述问题,最好在model中使用attr_accessible
代替attr_protected
。attr_accessible
罗列出model中可通过mass assignment设置的属性。
更新模的脸定义如下,仅允许name属性可通过mass assignment被设置。
ruby
class User < ActiveRecord::Base has_many :comments attr_accessible :name end
最后,log如下:
ruby
Processing UsersController#create (for 127.0.0.1 at 2009-02-04 20:39:15) [POST] Session ID: 48b9264e8da94d0a0edadce5e31ac500 Parameters: {"user"=>{"name"=>"hacker", "comment_ids"=>["1", "2"], "admin"=>"1"}, "action"=>"create", "controller"=>"users"} SQL (0.000307) INSERT INTO users ("name", "admin") VALUES(’hacker’, ’f’) Redirected to http://localhost:3000/users/show/9
原文:http://railscasts.com/episodes/26-hackers-love-mass-assignment?view=asciicast