Ruby
Rails

RailsのStrong Parametersを調べる

More than 1 year has passed since last update.

railsを触っていたらStrong Parametersというものが出てきたので調べてみました。

初心者ですので間違っている部分ありましたらご指摘いただければと思います。:eyes:


この記事で言いたいこと


  • 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


requireとpermitを軽く読み解く

https://github.com/rails/strong_parameters/blob/master/lib/action_controller/parameters.rb

を少し見てみました。


require


parameters.rb

def require(key)

self[key].presence || raise(ActionController::ParameterMissing.new(key))
end

presenceはrailsのObject#presenceメソッドです。

値が存在してればselfを返して、値が無ければfalseを返すようです。

requireメソッドでは、値がなければエラーを返すようになっていました。

また「[]」は以下のようにオーバーライドされており、ハッシュをパラメータに変換する機能を持たせてあります。


parameters.rb

def [](key)

convert_hashes_to_parameters(key, super)
end


permit


parameters.rb

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の部分に許可するパラメータの型が設定されています。


parameters.rb

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で指定していないパラメータはこちらで処理されます。


parameters.rb

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/environments/development.rb

config.action_controller.action_on_unpermitted_parameters = :raise


ActionController::UnpermittedParametersエラーが出力されるようになります。


参考