Rails4 | 新規・変更機能 | マスアサインメント機能と脆弱性とStrong Parameters
概要
マスアサインメント機能と脆弱性とStrong Parameters
マスアサインメント機能
RailsではDBの更新系処理で複数のカラムを一括で指定できます。
例えば
Person.new(name: 'hoge', age: 24)
person.update(name: 'hoge', age: 24)
など。
この便利な機能をマスアサインメント機能と呼びます。
マスアサインメントを利用する際の注意点
マスアサインメントはハッシュをそのまま利用します。
例えばユーザーからのリクエストは params 変数に Hash として保存されています。
この内容をそのままにマスアサインメント機能を利用して設定していた場合に、
想定していないカラムを更新されてしまう可能性があります。
例えばUserクラスに name, age, admin の3カラムがあり、
adminはユーザーの画面からは更新させない管理者権限だとします。
Userの新規登録処理で、以下のようなコードを書いていた場合、
user = User.new(params[:user])
user.save
不正リクエストなどによってparamsにadminが含まれていた場合、
管理者権限を任意の値に設定されてしまいます。
Rails3 までのマスアサインメント対策
もともとRailsのこの対応策を用意していました。
Model で更新対象のカラムを attr_accessible を利用して設定しておくのです。
class User < ActiveRecord::Base
attr_accessible :name, :age
end
これで、 name , age しか更新できなくなりました。
このような対策が Rails 側で用意されていたものの、
GitHubがマスアサインメントのセキュリティーホールを作ってしまったため、
クラックされてしまいました。
それを機に、この脆弱性の対策が注目されはじめました。
Rails3 までは Model 側で attr_accessible を利用していましたが
Rails4 では Controller 側で Strong Parameters を用いることでこの問題を防ぎます。
Rails4 のマスアサインメント対策
Rails4 では Controller 側の Strong Parameters でマスアサインメントの脆弱性に対応します。
以下は、scaffold によって生成された Strong Parameters のコードです。
def create
@person = Person.new(person_params)
# 略
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def person_params
params.require(:person).permit(:name, :age)
end
下記のように命名するのが慣例になっています。
def <Model名>_params
params.require(:<Model名>).permit(:<カラム名1>, :<カラム名2>)
end
マスアサインメント脆弱性をわざと作って、突破してみる
Person を scaffold します。
Personは name , age , admin を持ちます。
rails g scaffold person name:string age:integer admin:boolean
rake db:migrate
views/people/_form.html.erb から管理者のチェックボックスを削除します。
これで、一見管理者権限を設定できなくなったかのように見えます。
下記を削除
<div class="field">
<%= f.label :admin %><br>
<%= f.check_box :admin %>
</div>
Railsを起動して、 新規追加画面を開きます。
そのまま登録すると下記のようなデータになります。
例えば name = 'tanaka', age = 20 の場合が以下。
$ select * from people;
1|tanaka|20||2014-06-25 23:05:23.666879|2014-06-25 23:05:23.666879
次に、再度新規登録画面を開いて GoogleChrome のデベロッパーツールの
ElementタブでHTMLを直接編集します。
Age の設定をしている DIV タグを選択して、右クリック=>Edit HTMLを選びます。
そして、末尾に下記のHTMLを追加します。
<div class="field">
<label for="person_admin">Admin</label><br>
<input name="person[admin]" type="hidden" value="0"><input id="person_admin" name="person[admin]" type="checkbox" value="1">
</div>
すると、下記のように画面にチェックボックスが追加されたのでチェックを有効にして
不正リクエストを送信します。
追加が成功してしまいました。
DBを確認しますが、確かに管理者権限が「t」になっています。
$ select * from people;
1|tanaka|20||2014-06-25 23:05:23.666879|2014-06-25 23:05:23.666879
4|suzuki|19|t|2014-06-25 23:21:41.977131|2014-06-25 23:21:41.977131