脆弱性を攻撃してみよう (2) オープンリダイレクト

  • 29
    いいね
  • 0
    コメント

はじめに

Webアプリケーションにオープンリダイレクトの脆弱性があると、どのような攻撃を受けるでしょうか?実際にオープンリダイレクトを攻撃してみましょう。

※この記事は、バグだらけのWebアプリケーションを使って、いろいろな脆弱性とそれを攻撃、防御する方法を紹介する記事の2回目です。1回目の記事はここにあります。

攻撃してみよう

オープンリダイレクトについて詳しい説明する前に、オープンリダイレクトの脆弱性を攻撃してみましょう。

Webアプリケーションをここからダウンロードして、次のコマンドで起動します。

java -jar easybuggy.jar

※実行するにはJavaが必要です。詳細については、こちらのページを参照して下さい。

※この記事では、バージョン1.3.6を使用しています。今後のリリースで動作は変わる可能性があります。

起動したら、http://localhost:8080にアクセスして下さい。以下のような画面の真ん中に「脆弱性」と書かれたセクションがあります。

main.png

この中の上から10番目に「オープンリダイレクト可能なログイン画面」のリンクがあります。クリックすると、次のような画面が表示されます。

Screenshot from 2017-09-21 02-26-59.png

ユーザーIDに「admin」、パスワードに「password」を入力し、ログインボタンをクリックすると、次のような画面が表示されます。

Screenshot from 2017-09-21 02-23-27.png

これだけの機能ですが、オープンリダイレクトの脆弱性があります。注目するのは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」でログインします。

Screenshot from 2017-09-20 20-42-46.png

ログインできたでしょうか?ログインできたとは思いますが、その前に以下のようなログインエラー画面が表示されたはずです。

Screenshot from 2017-09-20 20-42-57.png

この時、再入力したユーザーIDとパスワードは外部のサイトに送信されていたことに気づきましたか?

ログインエラー画面のブラウザのURLを見て下さい。外部のサイトのURL(https://k-tamura.github.io/openred)になっています。ログインエラー画面が表示されましたが、実際にはログインは成功しており、このWebアプリケーションの画面に似せたエラー画面を持つ外部サイトに遷移していたわけです。ユーザーIDとパスワードを再入力してログインボタンをクリックした後、本来の遷移先にリダイレクトされているので、外部サイトに遷移したことに気づかなかった人もいたのではないでしょうか。

オープンリダイレクトとは

このように、オープンリダイレクトとは、Webアプリケーションのリダイレクト機能を悪用して、ユーザーを悪意のあるサイトに誘導する攻撃のことを言います。

攻撃者は、先ほどのようなリンクをユーザーにクリックさせて、気付かないうちにユーザーを攻撃者のサイトに誘導させます。さらに今回のように、精巧に偽せたログインエラー画面を外部サイトに用意して、ユーザーIDとパスワードを入力させようとするかもしれません(「フィッシング」)。今回はお試しアプリから悪意の無い私のサイト(https://k-tamura.github.io/)へのリダイレクトでしたが、これがオンラインバンキングから悪意のあるサイトへのリダイレクトであれば大変なことです。

openredirect.png

この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しかないのであれば、以下のような実装に修正するのが一番安全です。

    String gotoUrl = request.getParameter("goto");
    if ("/uid/serverinfo.jsp".equals(gotoUrl)) {
        res.sendRedirect(gotoUrl);
    } else {

しかし、そもそもこういった機能が本当に必要なのかを検討して、必要なければ別の方式を考えた方がいいかもしれません。

参考