Laravelのfillableやguardedがなぜ存在しているのかを、メモを兼ねて記事にしてみました。
結論:fillableは複数代入の脆弱性に対応するために必要
Laravel公式には以下のように書かれています。
createメソッドを使用する前に、モデルクラスでfillableまたはguardedプロパティを指定する必要があります。すべてのEloquentモデルはデフォルトで複数代入の脆弱性から保護されているため、こうしたプロパティが必須なのです。
しかし、初心者の僕にはよくわかりません。
複数代入の脆弱性とはなんでしょうか?
複数代入の脆弱性とは?
例えばユーザー情報を更新するための、下記のようなフォームがあったとします。
見てわかる通り、ユーザーが変更できるのは「ユーザー名」と「メールアドレス」のみです。「会員ランク」は確認のためだけに表示されています。
しかし、もしfillableやguardedの機能がLaravelに無かった場合、コードの書き方によっては、「会員ランク」を不正に変更されてしまう可能性があるのです。
では、どういうコードの書き方だと不正な変更に繋がってしまうのでしょうか?
それが、"複数代入"と呼ばれる下記のようなコードです。
pubic function update(Request $request,User $user)
{
$user->update($request->all());
}
問題となるのは、3行目の$request->all()
の箇所です。
$request->all()
を使用すると、フォームから送られてきたリクエストのデータが自動で配列になります。そしてupdateメソッドにより、その配列の各要素に対応するDBの値が更新されます。
このとき、普通であれば、更新されるのはフォームに入力欄のある「ユーザー名」と「メールアドレス」だけです。
しかし、もしユーザーがブラウザの機能を使って入力項目を不正に増やした場合、「会員ランク」などの、本来であればユーザーが操作できない値までもが更新されてしまう可能性があるのです。
これが、「複数代入の脆弱性」です。
Laravelは、複数代入の脆弱性にデフォルトで対策できている
前置きが長くなりましたが、この"複数代入の脆弱性"への対策となるLaravelの機能がfillableやguardedです。
上にも書かせて頂いた通り、複数代入の脆弱性は、リクエストのデータを配列にすることで発生します。そのためLaravelでは、配列を使ってモデルを生成・更新するメソッドは、自動でfillableかguardedによるチェックを受けるようになっています。
「配列を使ってモデルを生成・更新するメソッド」とは、代表的なもので言うと下記の3つです。
・createメソッド
・updateメソッド
・fillメソッド(saveメソッドと併せて使用)
つまり、モデルクラスに以下のようにfillableを定義すると「ユーザー名(name)」と「メールアドレス(email)」カラムはcreateやupdateやfillメソッドによる値の登録・書き換えが可能(それ以外のカラムは不可)という状態を作成することができます。
class User extends Model
{
protected $fillable = ['name', 'email'];
}
//「複数代入によって書き換えられたくないカラム」はここには入れないよう注意。
そして逆に言えば、上に挙げたような「配列を使ってモデルを生成・更新するメソッド」を使用せずにモデルを生成・更新する場合は、複数代入による脆弱性は発生しないので、fillableやguardedによるチェックも入りません。
fillableやguardedによるチェックが入らないケース
コードとしては、例えば下記のように、createやupdateやfillメソッドを使わずに、モデルの各カラムに対して1項目ずつデータを代入していった場合には、fillableやguardedによるチェックが入りません。
pubic function update(Request $request,User $user)
{
$user->name = $request->name;
$user->email = $request->email;
$user->save();
}
// ユーザーがブラウザの機能を使って入力項目を不正に増やしたとしても
// その入力項目をモデルに代入する処理が無いのでDBの値が不正に変更される心配もない。
もし複数代入による脆弱性以外の、コード上のミスや、その他の予期せぬ操作への対策としてfillableやguardedのチェックを通したい場合で、なおかつ何らかの理由で上記のように1項目ずつの代入が必要な場合は、下記コードのようにcreateやupdateやfillメソッドの中に、配列として1項目ずつの代入を記述します。
pubic function update(Request $request,User $user)
{
$user->update([
'name' => $request->name,
'email' => $request->email,
]);
}
pubic function update(Request $request,User $user)
{
$user->fill([
'name' => $request->name,
'email' => $request->email,
])->save();
}
本記事は以上となります。何かおかしな部分や間違った記述などありましたら教えて頂けると嬉しいです!