APIセキュリティ入門:REST・認証・認可
APIのエラー応答から漏れる情報
この回でやること
スタックトレースや内部のパス、SQL文、そして404と403の使い分け。APIのエラー応答から内部構造や存在が漏れる経路と、その塞ぎ方を扱います。
- 読む 約 7 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
エラー応答は、認可が拒んだときや処理が失敗したときにAPIが返す本文とステータスコードです。この回は、その応答そのものから内部の情報が漏れる経路と、塞ぎ方を扱います。
読み終えるころには、他人の番号と無い番号を送り分けて、返りの違いから何が読めるかを自分で確かめられるようになります。
開発中の設定のまま出るもの
APIはエラーも本文で返します。開発中に便利なようにと、失敗の詳細をそのまま返す設定が残っていると、本番でこう返ることがあります。
JSON
{"error": "db error",
"query": "SELECT * FROM orders WHERE id = 1044",
"file": "/app/handlers/orders.rb",
"trace": "..."}ここから読めるものは多岐にわたります。組み立てられたSQL文、テーブルとカラムの名前、サーバー上のファイルの位置、使っているライブラリの種類です。攻撃者にとっては、次にどこを押せばよいかを教える地図になります。SQLインジェクション編で扱った探りも、こうした応答があれば一気に楽になります。
存在は、コードの違いから漏れる
もう一つの漏れは、もっと静かです。認可で拒むときのステータスコードの選び方から、対象の存在が読めてしまいます。エンドポイントの回で見たとおり、注文の番号は推測できます。推測した番号を送ったとき、返りがこう割れていたらどうでしょう。
プレーンテキスト
GET /api/orders/1044 他人の注文 → 403 あるが見せない
GET /api/orders/9999 無い注文 → 404 無い403と404を律儀に返し分けると、中身を一切見せていなくても、どの番号が実在するかが分かります。 番号を順に送るだけで、存在する注文の一覧が手に入ります。存在そのものを隠したい対象では、見せられない場合も404に揃える、という判断を取ります。ただし認証が切れている場合の401など、区別が必要な場面もあるので、何を隠したいかを決めてから揃えます。
エラーを詳しく返すのは開発者のためですが、その詳しさは攻撃者にそのまま渡ります。詳細は本番では返さず、サーバー側のログにだけ残します。
どう直すか
本番ではスタックトレースや内部のパス、SQL文を応答に含めません。利用者に返すのは、何が起きたかを大まかに伝える短いメッセージだけにし、詳細はサーバーのログに書きます。存在を隠したい対象は404にそろえ、返し分けから実在が読めないようにします。
手を動かすときは、練習用の環境で、わざと壊れた入力や他人の番号を送り、返る本文とステータスコードを記録してください。SQL文やファイルの位置が出ていないか、存在の有無で403と404に割れていないかを見ます。