
[laravel8] mass assignment
LaravelではEloquentモデルのcreateメソッドを使用してデータを保存すると、保存したモデルのインスタンスを返してくれます。
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
しかし、この方法は『mass assignmentの脆弱性』を招くとして知られていて、データベースのテーブルのカラムが書き換えられてしまう可能性があります。
mass assginment の例
例えばUserのControllerで次のようにcreateメソッドを使用したとします。Formから受け取った値をcreateメソッドを使いデータベースに保存します。
<?php
public function create(Request $request){
$user = User::create( $request->all() );
}
しかし、もし悪意を持ったユーザーがブラウザのdev toolなどを使い次のフィールドを埋め込んでしまったらどうなるでしょうか?
<input type="hidden" name="is_admin" value="1">
もしユーザーのテーブルにis_adminのカラムが存在していれば、このユーザーは管理者権限を手に入れ管理者ページへ侵入できるかもしれません。
このような事を防ぐためにlaravelのeloquentモデルでは$fillableと$guardedのプロパティが良いされています。
fillable
$fillableプロパティはmass assginment、つまり一括でデータを挿入する時に、挿入できるカラムを定義します。
次のコードを見てみましょう。
Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'excerpt', 'body'];
}
title, excerpt,bodyのカラムはmass assignmentできることを定義しました。
tinkerを使って確認してみると
$php artisan tinker
Psy Shell v0.10.8 (PHP 8.0.3 — cli) by Justin Hileman
>>> use App\Models\Post;
>>> Post::create(['title' => 'test title', 'excerpt' => 'asdfa asdf a fasdf a', 'body' => 'body body body']);
=> App\Models\Post {#4223
title: "test title",
excerpt: "asdfa asdf a fasdf a",
body: "body body body",
updated_at: "2021-05-17 11:21:30",
created_at: "2021-05-17 11:21:30",
id: 3,
}
>>>
エラーもなくcreateで保存できています。
では、idを一緒に挿入できるか試してみましょう。
>>> Post::create(['id' => 1000, 'title' => 'test title2', 'excerpt' => 'asdfa2 asd2f 2a2 2fasdf a', 'body' => 'body2 body2 body2']);
=> App\Models\Post {#4279
title: "test title2",
excerpt: "asdfa2 asd2f 2a2 2fasdf a",
body: "body2 body2 body2",
updated_at: "2021-05-17 11:24:44",
created_at: "2021-05-17 11:24:44",
id: 4,
}
>>>
'id' => 1000, を追加しましたが、作成されたインスタンスのidは4になっていますね。
guarded
$guardedプロパティは$fillableの逆になります。$fillableの時はmass assignmentできるカラムを指定しましたが、$guardedはmass assignmentできないカラムを指定します。
Post.phpを次のようにidカラムをガードするように変更しました。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $guarded = ['id'];
}
tinkerでcreateメソッドを実行してみます。
>>> use App\Models\Post;>>> Post::create(['id' => 1000, 'title' => 'test title3', 'excerpt' => '3333333f a', 'body' => 'body3 body3 body3']);
=> App\Models\Post {#4225
title: "test title3",
excerpt: "3333333f a",
body: "body3 body3 body3",
updated_at: "2021-05-17 11:32:50",
created_at: "2021-05-17 11:32:50",
id: 5,
}
idを1000で挿入を試みましたが、作成されたインスタンスではidが5になっていますね。idが守られている事がわかりました。
最後に
laravelはこのように悪意のあるユーザーから守ってくれる機能を標準で準備しています。しかし、フォームの値などを保存する時は必ずバリデーションを行い、データをコントロールすることを心がけましょう。