昨日 Laravel にセキュリティの問題が発覚して修正版がリリースされました。それぞれ 6.20.11, 7.30.2, 8.22.1 のバージョンでこの問題は修正され、これらのバージョン以降の Laravel については問題ありません。
Security: Laravel 6.20.11, 7.30.2, 8.22.1 Released – The Laravel Blog
composer update
で全部アップデートなりcomposer update laravel/framework
で Laravel と Laravel が依存するパッケージのみアップデートなりするべきです。
問題の内容は意図せぬバインド値がプレースホルダに割り当てられる場合があったことです。
具体的に何が起きていたかというと次です。次の例は Laravel Security Advisory – January 13 2021 | Kane Cohen から引用しました。
// HTTP Request Query: https://laravel.com/users?id[]=1&id[]=1 $id = Request::input('id'); User::where('id', $id)->where('is_admin', 0)->first(); // これは、"is_admin "カラムに1が割り当てられているクエリにつながる可能性があります。
where の value にあたる部分に配列を渡すと一つのバインド値となってほしい部分に複数のバインド値が入ります。
グローバルスコープなしのUserモデルを用意して、実際に旧版で上述のコードを動かすと、クエリビルダは次の状態でプレースホルダ付き SQL とバインド値を保持し、
# プレースホルダ付き SQL select * from `users` where `id` = ? and `is_admin` = ? limit 1 # バインド値 array:3 [ 0 => 1 1 => 1 # ←余分な 1 が入ります 2 => 0 ]
次のエラーになります。
"SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (SQL: select * from `users` where `id` = 1 and `is_admin` = 1 limit 1)"
多くの正常なコードではこの様にプレースホルダの数とバインド値の数が異なるでしょうから意図せぬ 500 エラーで済みます。もしバリデーションが適切になされているのであれば、配列型の値が一つのバインド値となることを期待する値に入らないので安全です。とはいえ Laravel 自体をアップデートしておくのが無難です。
セキュリティ的なエラーにつながるのは例えば次です。
$id = [1, 1]; // 外部のリクエストから得られる値 User::where('id', $id) ->whereRaw('is_admin = ?')// バインド値としての 0 の指定忘れ ->first();
こうすると次のようになり
# プレースホルダ付き SQL select * from `users` where `id` = ? and `is_admin` = ? limit 1 # バインド値 array:3 [ 0 => 1 1 => 1 # ←余分な 1 が入ります # ←に正常なコードでは 2 => 0 が入っていました ]
次のクエリが走ります。
select * from `users` where `id` = 1 and is_admin = 1 limit 1
バインド値の数とプレースホルダの数を一致させることができたため SQL として正しいクエリが走り、管理者であるユーザが取得できてしまいました。
旧版ではこの様に外部から意図しないクエリを走らせることができるパターンがあります。