起動したら、http://localhost:8080にアクセスして下さい。以下のような画面の真ん中に「脆弱性」と書かれたセクションがあります。
この中の上から10番目に「オープンリダイレクト可能なログイン画面」のリンクがあります。クリックすると、次のような画面が表示されます。
ユーザーIDに「admin
」、パスワードに「password
」を入力し、ログインボタンをクリックすると、次のような画面が表示されます。
これだけの機能ですが、オープンリダイレクトの脆弱性があります。注目するのはURLです。最初にアクセスしたログイン画面のURLはhttp://localhost:8080/openredirect/login?goto=/uid/serverinfo.jsp
で、ログイン後のURLはhttp://localhost:8080/uid/serverinfo.jsp
となっています。goto=/uid/serverinfo.jsp
というクエリストリングで、ログイン後の遷移先を指定しているのが推測できるかと思います。
いったんログアウトして、今度は以下のリンクをクリックして下さい。
http://localhost:8080/openredirect/login?goto=%2f%2f%6b%2d%74%61%6d%75%72%61%2e%67%69%74%68%75%62%2e%69%6f%2f%6f%70%65%6e%72%65%64
先ほどと同じログイン画面が表示されます。再度「admin
」、「password
」でログインします。
ログインできたでしょうか?ログインできたとは思いますが、その前に以下のようなログインエラー画面が表示されたはずです。
この時、再入力したユーザーIDとパスワードは外部のサイトに送信されていたことに気づきましたか?
ログインエラー画面のブラウザのURLを見て下さい。外部のサイトのURL(https://k-tamura.github.io/openred
)になっています。ログインエラー画面が表示されましたが、実際にはログインは成功しており、このWebアプリケーションの画面に似せたエラー画面を持つ外部サイトに遷移していたわけです。ユーザーIDとパスワードを再入力してログインボタンをクリックした後、本来の遷移先にリダイレクトされているので、外部サイトに遷移したことに気づかなかった人もいたのではないでしょうか。
オープンリダイレクトとは
このように、オープンリダイレクトとは、Webアプリケーションのリダイレクト機能を悪用して、ユーザーを悪意のあるサイトに誘導する攻撃のことを言います。
攻撃者は、先ほどのようなリンクをユーザーにクリックさせて、気付かないうちにユーザーを攻撃者のサイトに誘導させます。さらに今回のように、精巧に偽せたログインエラー画面を外部サイトに用意して、ユーザーIDとパスワードを入力させようとするかもしれません(「フィッシング」)。今回はお試しアプリから悪意の無い私のサイト(https://k-tamura.github.io/
)へのリダイレクトでしたが、これがオンラインバンキングから悪意のあるサイトへのリダイレクトであれば大変なことです。
このWebアプリケーション(EasyBuggy)のように、リクエストパラメータなどで指定したURLにユーザーをリダイレクトする機能を持っているWebアプリケーションには、オープンリダイレクトの脆弱性が存在する可能性があります。
ただし、Webアプリケーションが想定外のURLにユーザーをリダイレクトしないように、チェックしていれば問題はありません。チェックせずに任意のURLへリダイレクトしているのであれば、攻撃者にリクエストパラメータを書き換えられて、ユーザーを危険なサイトへと誘導されてしまいます。
どのような実装になっているか
今回試したログイン機能は、どのような実装になっているのでしょうか。ソースコードを見てみましょう。重要な部分は以下です。
String gotoUrl = request.getParameter("goto");
if (gotoUrl != null) {
response.sendRedirect(gotoUrl);
} else {
リクエストパラメータgoto
の値を検証せずに、sendRedirect
しています。
オープンリダイレクトの対策
では、どのような対策をすればいいでしょうか?
対策はシンプルで、リダイレクト先が遷移先として適切なURLであるかをチェックすることです。ただし、気をつけないといけないのは、チェック処理の実装方法です。
外部サイトへのリダイレクトを禁止する目的で「http
」で始まるURLを拒否したとしても、前述の例のように「//k-tamura.github.io/openred
」をほとんどのブラウザが「http://k-tamura.github.io/openred
」と解釈するので、適切な対策とはいえません。許可できるリダイレクト先が明確にリストアップされているのであれば、それだけを完全一致でチェックするような実装をした方が、部分一致や正規表現でチェックするような実装よりも安全と考えられます。
例えば、リダイレクト先が/uid/serverinfo.jsp
と/uid/clientinfo.jsp
しかないのであれば、以下のような実装に修正するのが一番安全です。
String gotoUrl = request.getParameter("goto");
if ("/uid/serverinfo.jsp".equals(gotoUrl) || "/uid/clientinfo.jsp".equals(gotoUrl)) {
res.sendRedirect(gotoUrl);
} else {
しかし、そもそもこういった機能が本当に必要なのかを検討して、必要なければ別の方式を考えた方がいいかもしれません。
参考