はじめに
Laravelでユーザー登録画面を作成しています。
ログインIDをテーブルに登録する際にAES_ENCRYPTで暗号化したいので、以下のように実装してみました。
- /app/Http/Controllers/UserController.php
PHP
1// ~省略~ 2 public function store(CreateUserRequest $request) { 3 $user = new User; 4 5 $user->setLoginIdAttribute($request->login_id); // 暗号化 6 // ~省略~ 7 8 $user->save(); 9 10 return view('user.complete'); 11 } 12// ~省略~
- /app/Models/User.php
PHP
1// ~省略~ 2use Illuminate\Database\Eloquent\Builder; 3use Illuminate\Support\Facades\DB; 4 5class User extends Model { 6 // ~省略~ 7 public function setLoginIdAttribute($value) { 8 $encryption_key = ENCRYPTION_KEY; // 定数 9 10 // TODO: この方法ではSQLインジェクションの心配があるため、対策を考えたい。 11 $this->attributes['login_id'] = DB::raw("HEX(AES_ENCRYPT('" . $value . "', '" . $encryption_key . "'))"); 12 } 13}
質問
この方法だとSQLインジェクションの心配が出てくると考えました。
※$value
の値がエスケープされずに直接入れているため。
そのため、プレースホルダーを用いてエスケープしたいのですが、
DB:raw()
でプレースホルダーを使用する方法はありますでしょうか?
もしくは、DB::raw()
を使用しない もっといい方法がないでしょうか?
もしかすると初歩的な質問なのかもしれませんが、ご教示ください。
動かしている環境
- Laravel:6.5
- PHP:7.3
- MySQL:8.0
>ログインIDをテーブルに登録する際にAES_ENCRYPTで暗号化してほしいとのこと
どこからの依頼でしょうか。
>SQLインジェクションの心配が出てくると考えました
心配はいいとして、実際に試してみましたか?
>どこからの依頼でしょうか。
この情報が必要な理由をお聞きしてもよろしいでしょうか?
ひとまず、この暗号化は行わなければならない前提で回答を考えていただけますと幸いです。
>心配はいいとして、実際に試してみましたか?
試してみましたが、構文エラーでできませんでした。
※私のやり方が間違っている可能性もありますが。
ちなみに$valueの値が以下になるようにやってみました。
`','')),~INSERT文のSQLが成り立つように記載~); DELETE FROM [テーブル名] WHERE id = [存在するID]; --`
>この情報が必要な理由をお聞きしてもよろしいでしょうか?
もし業務関係である場合、質問サイトを頼って解決することは質問ではなく作業依頼となるからです。
本来、報酬が発生するものを無料で片付けようという魂胆があるのでしたら誰も助けようとはしません。
なるほど、確かにそうですね。
質問に至った経緯としましては、以下です。
1. 仕事で似たような依頼を受ける
2. この問題に直面して、結局「はじめに」で書いた方法を諦める
3. (ここからプライベート)この問題を解決する方法はなかったのかと調べ始める
4. 質問する
ただし、ここで知識を得てそれを今後仕事で使用しないとは限りませんので、
この質問は削除します。
得た知識技術を使うことに問題はないと思います。それがダメだと検索すらしてはダメということになります。
あくまで作業依頼のような形になっているとマズいというだけです。表現調整すれば良いと思います。
>あくまで作業依頼のような形になっているとマズいというだけです。表現調整すれば良いと思います。
かしこまりました。表現を変更してみました。
提示のコードではSQLインジェクションは発生しませんが、なぜそう思ったのでしょう?
>提示のコードではSQLインジェクションは発生しませんが、なぜそう思ったのでしょう?
プレースホルダーを用いずにエスケープされていない`$value`をSQLに直接組み込んでいるため、
`$value`の値の内容によってはSQLインジェクションが起きると考えました。
SQLインジェクションが発生しないとのことですが、根拠(「実はLaravelでは〇〇の部分で既にエスケープされている」など)があればご教示いただくことは可能でしょうか?
$user->save();
でプレースホルダーを使う処理がなされます
つまり、以下であった場合、
```
$user = new User;
$user->name = 'hoge'
$user->save();
```
SQL文は`INSERT INTO users (name) VALUES (?);`になるということでしょうか?
そうなりますと、上の例であれば、`INSERT INTO users (login_id) VALUES (?);`になるということでしょうか?
そういうことです。
ただコードを見る限り、要件を満たしていないと思われます。
kawaxさんの回答にもコメントしましたが
ユーザーIDを暗号化すると、ログイン処理時にどのレコードが該当ユーザかわからなくなるため
全レコードを1個ずつ復元して対応していかないといけない気がします。
ユーザー数が多くなるにつれてログイン時間が肥大化していきます
>そういうことです。
そうなると、login_idカラムは`HEX(AES_ENCRYPT('HOGE'', 'FUGA'))`という文字列が登録されると思いますが、実際に実行したところ暗号化された文字列がテーブルに登録されていました。
これは、プレースホルダーを用いてもエスケープされていないということでしょうか?
>ただコードを見る限り、要件を満たしていないと思われます。
すみません、要件について詳しくご教示いただけますでしょうか?
> 実際に実行したところ暗号化された文字列がテーブルに登録されていました。
これは、プレースホルダーを用いてもエスケープされていないということでしょうか?
言葉で言われても、多くの質問者が事実を述べているかは回答者に伝わりません(質問者、または回答者が事実誤認をしていたり、間違って覚えていたりする)ので、エビデンスを示してくれませんか?
>言葉で言われても、多くの質問者が事実を述べているかは回答者に伝わりません(質問者、または回答者が事実誤認をしていたり、間違って覚えていたりする)ので、エビデンスを示してくれませんか?
かしこまりました。
今、手元に動かせる環境がないので、後日とさせてください。
エビデンス(共有すべき情報)としては、
`$request->login_id`に入っている(入れた)値、実際にテーブルに格納された値の2つがあればよろしいでしょうか。
他にあったほうがよい情報などございますでしょうか?
mikkameさん
ご回答ありがとうございます。
>ユーザーIDを暗号化すると、ログイン処理時にどのレコードが該当ユーザかわからなくなるため
>全レコードを1個ずつ復元して対応していかないといけない気がします。
>ユーザー数が多くなるにつれてログイン時間が肥大化していきます
まったくもってその通りだと思います。
この方法で暗号化を行う必要がないこと や 暗号化することによってのデメリットがあることは理解できました。
> `$request->login_id`に入っている(入れた)値、実際にテーブルに格納された値の2つがあればよろしいでしょうか。
そうですね。その結果追加で提示してもらいたいものが出てくるかもしれませんが、よろしくお願いします。
Kosuke_Shibuyaさん
入力した値、encryption_key、結果はそれぞれ以下となります。
login_id:loginidtest
encryption_key:encryptionkey
結果:06AED6A088C61619BCCCC0EBDBA86AA3
また、以下のようなSQLを実行したところ、挿入された値の結果は同じものとなりました。
```
INSERT INTO users (login_id, password) VALUES
(HEX(AES_ENCRYPT('loginidtest', 'encryptionkey'), 'password');
```
結果:06AED6A088C61619BCCCC0EBDBA86AA3
上記の結果から、エスケープされていないと考えました。