[Laravel5] Eloquent ModelのMass Assignment時に複数のホワイトリストを使い分ける
こんにちは、中榮です。
Eloquent ModelのMass Assignmentの設定
EloquentのModelのMass Assignmentの設定の肝は、ブラックリストの設定である$guardedプロパティとホワイトリストの設定である$fillableプロパティです。
この2つを設定することでModel::fill()の引数に与えられた連想配列のうち、どの項目をModelにAssignできるかが決まります。
とある項目がMass Assignment可能かどうかはModel::isFillable()でチェックされており、
/**
* Determine if the given attribute may be mass assigned.
*
* @param string $key
* @return bool
*/
public function isFillable($key)
{
if (static::$unguarded) {
return true;
}
// If the key is in the "fillable" array, we can of course assume that it's
// a fillable attribute. Otherwise, we will check the guarded array when
// we need to determine if the attribute is black-listed on the model.
if (in_array($key, $this->fillable)) {
return true;
}
if ($this->isGuarded($key)) {
return false;
}
return empty($this->fillable) && ! Str::startsWith($key, '_');
}
(※コードはLaravel 5.1 LTSからの引用)
- unguaded状態なら無条件で可
- 項目名が$fillableに列挙されていれば可
- $guadedに列挙されていれば不可
- $fillableが空でなければ不可
- 項目名がアンダースコアで始まっていれば不可
- その他は可
というフローでチェックされます。
guardedの初期設定はアスタリスクのみの配列(=全項目がブラックリスト)、fillableの初期設定は空配列(=ホワイトリストが空)となっています。初期設定ではMass Assignmentは全くできない状態です。
一般的に、ブラックリスト方式よりもホワイトリスト方式で入力をフィルターした方がセキュアなので、 実用的には$fillableのホワイトリストに最低限の許可を与えるのが良い習慣だと思います。
ホワイトリストを場合に応じて変えたい時
CRUDだけのアプリケーションなど、モデルの新規作成・編集フォームが1つだけという簡単な場合は 該当のModelクラスに直接$fillableを設定すれば良いと思います。
少し複雑なアプリケーションだと、権限に応じて入力可能な項目を変更する場合、編集フォームが複数ある場合など、フィルタする入力項目を変えたいケースがよくあります。
$fillableはprotectedプロパティなので直接変更することはできませんが、Model::fillable()メソッドによりインスタンスごとに設定が可能です。
/**
* Set the fillable attributes for the model.
*
* @param array $fillable
* @return $this
*/
public function fillable(array $fillable)
{
$this->fillable = $fillable;
return $this;
}
よりセキュアに
Modelの$fillableを空配列のままにしておき、Mass Assignmentのは逐一Model::fillable()を呼び出すことで、 必要以上の入力項目がModelのインスタンスに渡ることを防げます。
RailsのStrong Parametersと似たような発想ですね。
セキュリティリスクや不具合の低減には役に立ちます。
その分一手間増えるので、「Modelが更新されないと思ったらfillable()呼んでなかった!!!」という罠にはまる可能性は大いにあるので気をつけてください。