前提
- Windows home 10
- Laravel 8.x
- PHP 8.1
背景
-
Laravelでバリデーションを作っていた時の話。
-
Validatorクラスを使って以下のように書いていた。
$validator = Validator::make($request->all(), [
'product_id' => ['required', 'exists:products,id', 'integer', 'digits_between:1,9'],
]);
if ($validator->fails()) {
return redirect()->route('...')
->withErrors(['error' => '不正な操作です。']);
}
- バリデーションルールとして初めて
exists
を使用した- 指定したリクエスト値がDB内にあるか否かをチェックしてくれるので、結構便利
問題点
- ただ、上記の
exists
の使い方は問題があった。それは
他のバリデーションルールを通す前にDBにアクセスしてしまっている点
が問題だった
- これにより、SQLインジェクションを引き起こしかねないプログラムを書いたことになる
具体的にどんな問題があるかというと、
- まず前提として
exists
はDBにアクセスするため、クエリを発行する - そして、上記のコードだとバリデーションルールが
'required'
→ 'exists:...'
→ 'integer'
→ 'digits_between:1,9'
の順番に適用される
- 今回でいうと、
exists
でDBアクセスする際にはproduct_id
はinteger
であり、'digits_between:1,9'
でなければいけない - しかし、今のままでは配列でも、文字列でもDBアクセスできてしまう状態になっているのが一番の問題点
対策
- 正しくは以下のように
exists
をルールの最後に記述しなくてはいけなかった
$validator = Validator::make($request->all(), [
'product_id' => ['required', 'integer', 'digits_between:1,9''required', 'integer', 'digits_between:1,9', 'exists:products,id'],
]);
- これにより、リクエスト内にある
’product_id’
は
'required'
→ 'integer'
→ 'digits_between:1,9'
を通ったのちにexists
でクエリを発行するので、セキュリティ上安全なクエリが発行され、問題なくexists
が使えるようになります。
所見
-
「
exists
を使うとDBの値の有無チェックをする」=「SQLを発行している」
ということに気づけば、当初の記述はしなかったですがバリデーションとクエリ発行のつながりは基本ないことが多いので、気づけませんでした。。。 -
自分がよく実感するのですが、フレームワークを使用しているとクエリへの意識の薄れは痛感しています。。。
-
今回の過ちにより、クエリへの意識が再認識できた点では非常にいい機会になりました!