2.

Laravel の $fillable / $guarded / $hidden の説明

編集
この記事の要点
  • Laravel Eloquent モデルのマスアサインメントとシリアライゼーション制御
  • $fillable: 許可する属性のホワイトリスト
  • $guarded: 禁止する属性のブラックリスト(空配列で全許可)
  • $hidden: JSON / 配列変換時に含めない属性(パスワード等)
  • $visible: 逆に表示するもののホワイトリスト

 

マスアサインメント保護

Laravel の User::create($request->all()) のように配列を一括代入する操作(マスアサインメント)は、攻撃者が任意のフィールドを送り込める脆弱性があります。$fillable / $guarded でこれを制御します。

$fillable(ホワイトリスト・推奨)

class User extends Model
{
    protected $fillable = [
        'name',
        'email',
        'phone',
    ];

    // role や is_admin は含まれない → 一括代入できない
}

// 使用
User::create([
    'name' => 'Alice',
    'email' => 'alice@example.com',
    'role' => 'admin',  // ← 無視される (fillable にないので)
]);

User::create($request->all());
// → name, email, phone のみセットされる、他は無視

$guarded(ブラックリスト)

class User extends Model
{
    // 'id', 'role' は一括代入禁止
    protected $guarded = ['id', 'role', 'is_admin'];
}

// 空配列で全許可 (危険、テスト用)
protected $guarded = [];

// ガード解除 (デバッグ用、本番禁止)
Model::unguard();

$fillable と $guarded はどちらか一方

  • $fillable 推奨: 許可するものを明示するほうが安全
  • $guarded: 多くを許可する場合、禁止するものだけ列挙
  • 両方使うと $fillable が優先

マスアサインメントが効くメソッド・効かないメソッド

メソッドマスアサインメント保護
Model::create($attrs)
$model->fill($attrs)
$model->update($attrs)
$model->save()×(直接代入は OK)
$model->forceFill($attrs)×(強制代入)
Model::forceCreate($attrs)×
個別代入 $model->name = ...×
// 個別代入は OK
$user = new User();
$user->name = "Alice";
$user->role = "admin";  // ← fillable に関係なくセットできる
$user->save();

// forceFill / forceCreate は意図的に保護をバイパス
$user = User::forceCreate([
    'name' => 'Admin',
    'role' => 'admin',
    'is_admin' => true
]);

$hidden(JSON シリアライゼーション)

toArray() / toJson() 時に含めない属性:

class User extends Model
{
    protected $hidden = [
        'password',
        'remember_token',
        'api_secret',
    ];
}

// 使用
$user = User::find(1);
return $user->toJson();
// → {"id":1, "name":"Alice", "email":"..."}
// password は含まれない

return $user;  // 自動的に JSON 化される (Resource Controller)
// → 同上、password 非表示

$visible(逆: 表示するもののホワイトリスト)

class User extends Model
{
    protected $visible = ['id', 'name', 'email'];
    // → これら以外は全て隠す
}

// $hidden と $visible は併用可、$hidden が優先

動的に変更

// 一時的に表示 / 非表示
$user = User::find(1);

$user->makeVisible('password');     // password を表示
$user->makeHidden(['name', 'email']); // 一時的に隠す

return $user->toJson();

その他の関連プロパティ

$casts(型変換)

class User extends Model
{
    protected $casts = [
        'is_admin' => 'boolean',
        'options' => 'array',  // JSON カラム → 配列に
        'birthday' => 'date',
        'last_login_at' => 'datetime',
        'price' => 'decimal:2',
    ];
}

// 利用
$user->is_admin    // → bool
$user->options     // → array (DB は JSON 文字列)
$user->birthday    // → Carbon インスタンス

$dates(廃止予定、$casts に統合)

// 古い書き方 (Laravel 6 以前)
protected $dates = ['created_at', 'updated_at', 'deleted_at'];

// 新しい書き方 (Laravel 7+)
protected $casts = [
    'created_at' => 'datetime',
    'updated_at' => 'datetime',
];

$appends(カスタム属性をシリアライズに含める)

class User extends Model
{
    protected $appends = ['full_name'];

    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}

// toJson() の結果
// {
//   "id": 1,
//   "first_name": "Alice",
//   "last_name": "Smith",
//   "full_name": "Alice Smith"  ← appends で追加
// }

標準的なモデル例

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    // 一括代入を許可するフィールド
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    // シリアライゼーションから除外
    protected $hidden = [
        'password',
        'remember_token',
    ];

    // 型変換
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',  // 自動でハッシュ化
    ];

    // 追加属性
    protected $appends = ['avatar_url'];

    public function getAvatarUrlAttribute()
    {
        return $this->avatar
            ? Storage::url($this->avatar)
            : asset('img/default-avatar.png');
    }
}

セキュリティ上の注意

  • $fillable の指定なしは MassAssignmentException 発生 (Laravel 5.5+)
  • $guarded = [] は危険、絶対使わない(本番では)
  • パスワード等の機密情報は必ず $hidden に
  • API レスポンス用には API Resources を使うほうが柔軟

API Resources(より柔軟)

// 専用のリソースクラス
class UserResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at->toIso8601String(),
            // フロント側に必要な情報だけ厳選
        ];
    }
}

// コントローラから
return new UserResource($user);
return UserResource::collection($users);

関連記事

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. モデルの作成
  2. $fillable $guarded $hiddenの説明
  3. テーブルの紐づけ
  4. 主キーの指定とインクリメント
  5. タイムスタンプ
  6. モデルでselect
  7. モデルでinsert
  8. モデルでupdate
  9. 現在値に加算する方法
  10. created_at/updated_atの別名指定