LoginSignup
295
263

More than 5 years have passed since last update.

RailsのStrong Parametersを調べる

Last updated at Posted at 2017-08-25

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エラーが出力されるようになります。

参考


295
263
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
295
263