railsを触っていたらStrong Parametersというものが出てきたので調べてみました。
初心者ですので間違っている部分ありましたらご指摘いただければと思います。
この記事で言いたいこと
- Strong ParametersはDBに入れる値を制限することで、不正なパラメータの入力を防ぐ仕組みであること
Strong Parametersの概要
Rails4系から追加された仕組みです。
Rails3系まではMassAssignmentを使っていたのだが、そこに脆弱性があったためStrong Parametersが導入されたとのことです。
簡単に言うとDBへ入れたり更新したりするパラメータを制限してくれる仕組みです。
MassAssignment脆弱性にまつわるニュース↓
https://www.infoq.com/jp/news/2012/03/GitHub-Compromised
基本的な使い方
例)```
params.require(:user).permit(:name, :email, :password)
①requireでPOSTで受け取る値のキーを設定
②permitで許可するカラムを設定
ここではname, email, password属性の値だけDBに入れるのを許可
adminカラムとか付随してても無視します。
ちなみにstrong parametersを使ってないとrailsさん:angry:にエラーを吐かれるので注意
![スクリーンショット 2017-08-25 11.32.24.png](https://qiita-image-store.s3.amazonaws.com/0/156024/04516e71-5cda-a878-213d-f9362bb6eb9d.png)
# requireとpermitを軽く読み解く
https://github.com/rails/strong_parameters/blob/master/lib/action_controller/parameters.rb
を少し見てみました。
## require
```ruby:parameters.rb
def require(key)
self[key].presence || raise(ActionController::ParameterMissing.new(key))
end
presenceはrailsのObject#presenceメソッドです。
値が存在してればselfを返して、値が無ければfalseを返すようです。
requireメソッドでは、値がなければエラーを返すようになっていました。
また「[]」は以下のようにオーバーライドされており、ハッシュをパラメータに変換する機能を持たせてあります。
def [](key)
convert_hashes_to_parameters(key, super)
end
permit
def permit(*filters)
params = self.class.new
filters.flatten.each do |filter|
case filter
when Symbol, String
permitted_scalar_filter(params, filter)
when Hash then
hash_filter(params, filter)
end
end
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
params.permit!
end
こちらは実際にDBに受け渡されるパラメータを一つずつフィルターにかける作業をしているようです。
またpermitに指定されたものがハッシュだった場合も、分解されそのパラメータをフィルターに書けます。
そのフィルターではパラメータの型が許可できるものかどうかを検証してくれています。
以下のFilterlingの部分に許可するパラメータの型が設定されています。
PERMITTED_SCALAR_TYPES = [
String,
Symbol,
NilClass,
Numeric,
TrueClass,
FalseClass,
Date,
Time,
# DateTimes are Dates, we document the type but avoid the redundant check.
StringIO,
IO,
ActionDispatch::Http::UploadedFile,
Rack::Test::UploadedFile,
]
また、permitで指定していないパラメータはこちらで処理されます。
def unpermitted_parameters!(params)
return unless self.class.action_on_unpermitted_parameters
unpermitted_keys = unpermitted_keys(params)
if unpermitted_keys.any?
case self.class.action_on_unpermitted_parameters
when :log
name = "unpermitted_parameters.action_controller"
ActiveSupport::Notifications.instrument(name, :keys => unpermitted_keys)
when :raise
raise ActionController::UnpermittedParameters.new(unpermitted_keys)
end
end
end
def unpermitted_keys(params)
self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
end
ログを取るかエラーを出すようになっています。
デフォルトではログが出るようになっており、
エラーを出力するためには別途設定する必要があります(後述)。
NEVER_UNPERMITTED_PARAMSにはrailsによって生成されるパラメータが入っており、それは除外されるようになっています。
その他のstrong parametersの使い方
ネストしたパラメータを使う時
params.require(:user).permit(:name, :email, recommend_book: [:user_id, :comment])
入れ子になったパラメータをPOSTするときはStrong Parameters側も設定しないといけないです。
指定したキーがないときにエラーを出さないようにする
params.fetch(:user, {}).permit(:name, :email, :password)
userパラメータがなかったときはActionController::ParameterMissingのエラーが起きるようになっています。
上記の設定をしてあるとuserパラメータの代わりに{}がデフォルト値として評価されるようになります。
違うパラメータが送られてきた時にエラーを出力する
各環境のconfigfileに設定します。
出力したい環境に応じて設定を記述するファイルは変えて下さい。
config.action_controller.action_on_unpermitted_parameters = :raise
ActionController::UnpermittedParametersエラーが出力されるようになります。