認証・認可の脆弱性:ログインとIDOR
ユーザー名の列挙 存在するIDを当てる
この回でやること
登録済みの利用者名だけを、パスワードを知らないまま選り分ける手口です。応答の文言・ステータス・長さの差から漏れる仕組みと、揃え方を扱います。
- 読む 約 7 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
ユーザー名の列挙は、パスワードを1つも知らないまま、登録済みのIDだけを選り分ける手口です。ログインの4段のうち、2つ目の「利用者を探す」段の結果が外から見えてしまうときに成り立ちます。
読み終えると、自分のサービスの応答が登録の有無を漏らしていないかを、3か所を見て確かめられるようになります。
差は3か所から漏れる
練習用のショップに、存在しないzzzzと、存在するtanakaで、どちらもでたらめなパスワードを送ります。返る応答を並べます。
プレーンテキスト
zzzz → 404 {"error":"そのユーザーは登録されていません"}
tanaka → 401 {"error":"パスワードが違います"}ここでは文言とステータスコードの2つが違います。どちらか一方でも違えば、登録の有無が読めます。3つ目は応答の長さです。文言が同じでも、片方だけ末尾に再送のリンクが付くといった作りだと、バイト数の差で分かります。
なぜそれほど困るのか
登録済みのIDが分かると、そのあとの総当たりの対象が一気に絞れます。1万件の候補のうち実在が80件と判明すれば、以降の試行は80件だけに集中できます。手間が2桁下がるので、列挙は攻撃の前段としてほぼ必ず行われます。
漏れるのはログイン画面だけではありません。会員登録の「そのメールアドレスは使われています」、パスワード再設定の「送信しました/そのアドレスは見つかりません」も同じ穴です。
そのとおりで、ここは使い勝手と正面からぶつかります。実務では、ログインでは差を消し、会員登録では消さない、という分け方をよく取ります。登録画面は差を消しても「登録できなかった」ことは伝わってしまうためです。代わりに登録画面には回数の制限を掛けます。
揃える
直し方は、2の段の結果を応答に出さないことに尽きます。
- 文言を1つにする。「利用者名またはパスワードが違います」
- ステータスコードを揃える。どちらも
401にする - 応答の本文の長さと、含めるリンクを揃える
文言とコードを揃えても、存在するIDのときだけパスワードの照合が走れば、その分だけ応答が遅くなります。これは次回扱います。