Rails4 | 新規・変更機能 | マスアサインメント機能と脆弱性とStrong Parameters

# Rails4 | 新規・変更機能 | マスアサインメント機能と脆弱性とStrong Parameters

## 概要

マスアサインメント機能と脆弱性とStrong Parameters

## マスアサインメント機能
RailsではDBの更新系処理で複数のカラムを一括で指定できます。

例えば

~~~ruby
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 を利用して設定しておくのです。

~~~ruby
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* のコードです。

~~~ruby
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
~~~

下記のように命名するのが慣例になっています。

~~~ruby
def <Model名>_params
  params.require(:<Model名>).permit(:<カラム名1>, :<カラム名2>)
end
~~~

## マスアサインメント脆弱性をわざと作って、突破してみる

Person を *scaffold* します。
Personは *name* , *age* , *admin* を持ちます。

~~~bash
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を起動して、 新規追加画面を開きます。

![math1.png](https://qiita-image-store.s3.amazonaws.com/0/43480/576ea693-8281-3e68-de85-a35574fa275d.png)

そのまま登録すると下記のようなデータになります。
例えば name = 'tanaka', age = 20 の場合が以下。

~~~bash
$ 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を追加します。

~~~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>
~~~

すると、下記のように画面にチェックボックスが追加されたのでチェックを有効にして
不正リクエストを送信します。

![math2.png](https://qiita-image-store.s3.amazonaws.com/0/43480/b5ba4c0e-bab0-d25b-8f3d-61baa7843541.png)

![math3.png](https://qiita-image-store.s3.amazonaws.com/0/43480/e1e5c82b-46e3-3b1f-3604-4eb1ee41ded8.png)

追加が成功してしまいました。

![math4.png](https://qiita-image-store.s3.amazonaws.com/0/43480/9d58eb2d-2b15-b724-414b-0805108f8c73.png)

DBを確認しますが、確かに管理者権限が「t」になっています。

~~~bash
$ 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
~~~