背景
- バグ改修をせっせとやっていた時の話
- その一つに「XSS発生の対応」があった
- その時に見事にやらかした話
- 言語はLaravel
バグ状況
状況はこんな感じ
- 管理画面でユーザーの注文一覧を見れる
- この各注文に管理者が「メモ」を書けるようにしていた
- この「メモ」がtextareaで書かれていた
- この「メモ」に以下のようなコードを書くとJSが実行されてしまうバグが発生していた
<script>alert('危険!')</script>
典型的なXSSですね
当初の実装方針
- 注文の一覧表示までの処理の流れはこう
- ユーザーが注文=注文テーブルへの保存処理
管理者が注文一覧を閲覧=注文テーブルからデータを取得
そのうえで、私の実装方針は
「じゃあ、1. の保存時にエスケープ処理して対応して終わりじゃん」
と思い、「メモ」のsave時にe()
(※)を実装しました。イメージは以下。
public function update(Request $request) { ... DB::beginTransaction(); try { $user->order->fill([ // e()でエスケープ処理で対応 'memo' => e($validated['memo']), ]); $authenticatedUser->cart->save(); DB::commit(); ... }
※e()
はLaravelのエスケープ処理メソッド
結論
もうすでに「エスケープされたデータがDBに保存される」という問題が発生しており、「注文一覧の出力時に、エスケープ処理された文字(特殊文字)がそのまま出力してしまう」という脅威を招いている。
ということで即修正。
本当の問題点
ただ、テストを全くしてないわけではなかった。
実際に「管理画面の注文一覧ではXSSは発生せず出力内容に問題はなかった」ことは確認済みだった。
- 実際にタスクが完了したのちに、再度確認してみると本当の問題点が発覚。
起こっていた問題点
実装箇所を間違えてテストをしていました。。。
原因①:なぜエスケープ処理したのか(なぜ)
- 「ソースコードを読み違えによる見落とし」が原因
- 本来改修すべきは注文新規登録(=
store
メソッド) - ただ、自分が今回実装した箇所は注文編集(=
update
メソッド) - テストしたのは改修をしたと思い込んでいる新規登録箇所
- ココでは何も追加実装をしていないので、当たり前のように正常に表示される
- 本来改修すべきは注文新規登録(=
原因②:なぜ実装箇所とテスト範囲が違うのか
「新規登録処理の中でupdateメソッドを通っている」
と思い込んで実装していた
原因③:なぜデバッグを怠ったのか
- 主に2点
- バグ改修の期限が迫っており、精神的に焦っていたため
- 最近デバッグせずとも実装できることが多かったので、驕った
再発防止
レビュー前のチェック項目の追加
- 「想定している処理をしているか、デバッグを使って確認したか」
- レビュー前には自作のチェックシートでテスト漏れがないか確認してます。
- これの追加し、必ず想定したソースコードを通って処理されていることをテストする
所見
なかなかしょぼいことしたな、と実感したので自戒
当初は「XSSの理解不足」でまとめようとしたら、問題はそこじゃなかったので記事の方向性を軌道修正しました。