Androidアプリセキュリティ〜WebViewの注意点(1)〜

多くのアプリケーションで使用されているWebViewの脆弱性や取り扱う場合の注意点などを解説します。

WebViewとその脆弱性とは

WebViewとはWebページを表示するための画面部品です。簡単にアプリケーション内にWebページを表示できるため多くのアプリケーションで使用されていますが、外部とのインタフェースを持つ機能であることや使用頻度が多いことに起因してか報告されている脆弱性の数も多くなっています。
実際に報告された脆弱性を見てみましょう。これまで発表されたWebViewに関する脆弱性の実例についてJVN iPedia(※1)を見てみると、以下のような脆弱性が確認できます。

  • Sleipnir Mobile for Android における WebView クラスに関する脆弱性
  • Yahoo!ブラウザーにおける WebView クラスに関する脆弱性
  • Dolphin Browser における WebView クラスに関する脆弱性
  • iLunascape for Android における WebView クラスに関する脆弱性
  • TwitRocker2 (Android 版) における WebView クラスに関する脆弱性
  • 複数のクックパッド製 Android アプリにおける WebView クラスに関する脆弱性
  • 複数のGREE製AndroidアプリにおけるWebViewクラスに関する脆弱性
  • サイボウズLive for Android における WebView クラスに関する脆弱性
  • サイボウズ KUNAI for Android における WebView クラスに関する脆弱性
  • KUNAI Browser for Remote Service β における WebView クラスに関する脆弱性
  • Android 版 jigbrowser+ における WebView クラスに関する脆弱性
  • Boat Browser および Boat Browser Mini における WebView クラスに関する脆弱性

(2013年1月10日現在 JVN iPediaの検索結果より引用)

(※1) JVN iPediaとは
JVN(Japan vulnerability Note、JPCERT/CCとIPAが共同運営している脆弱性情報のポータ ルサイト)で公開されている脆弱性対策情報データベースのこと。
URL:http://jvndb.jvn.jp/index.html

これらの脆弱性は「当該製品のデータ領域にある情報が漏えいする可能性がある」と説明されていますが、これはWebViewの悪用されやすい仕様が原因になっているのではないかと考えています。
本稿ではWebViewには悪用されやすい仕様が存在し、アプリ作成時に注意を払わないと脆弱性が出来てしまう可能性があることと、その仕組みと対策についてご紹介したいと思います。

Androidのサンドボックスとデータ漏えいの一例

脆弱性の仕組みを説明するにあたって、まずAndroid OSのサンドボックス機能についてご紹介します。

Android OSのサンドボックス機能では、アプリケーションをインストールすると固有のユーザID、グループID、専用のディレクトリが割り振られ、専用のディレクトリ配下に固有のデータが保存されます。専用のディレクトリには通常他のユーザID/グループIDが割り振られたアプリからはアクセスできません。

このサンドボックス機能によって、アプリケーション固有のデータファイルが保護されています。例えば下記の図1の場合、サンドボックス機能によってアプリBから直接アプリAのデータにアクセスすることはできません。

図1. Androidのサンドボックス

ところが、アプリケーションに脆弱性が存在し悪用された場合にはこのサンドボックス機能が無効化され、固有のファイルにアクセスされてしまう可能性があります。

下記の図2をご覧ください。図2はアプリAのロジック部に存在する脆弱性をアプリBから悪用され、固有のデータにアクセスされてしまう仕組みをイメージにしたものです。

前述のサンドボックス機能によってアプリBから直接アプリAのデータにアクセスすることはできませんが、この場合アプリAのデータにアクセスしているのはアプリAのロジック部であり、アプリBはアプリAのロジック部とやり取りをしているだけになるため、間接的にアプリBからアプリAの固有のファイルへのアクセスが可能になります。

図2. 脆弱性があった場合のファイルアクセスの経路

WebViewの仕様が悪用された脆弱性の具体例と対策

次に具体的にWebViewの機能にあてはめて考えてみましょう。

WebViewは特に何も設定しない初期状態で「file://」というファイルに直接アクセスするためのスキームを扱うことができます(「file://」スキームはWebView固有のものではなくPCの世界でも利用されるスキームです)。

この「file://」スキームを利用しWebViewのアクセス先として「/data/data/[APP_NAME]/[ファイル名]」のようにアプリケーション固有のファイルを指定すると、WebView内にそのファイルの内容が表示されます。

この動作はWebViewの正常な動作であり仕様ですが、例えばこれを悪用されると、他アプリから発行されたIntentからアクセス先を受け取り、その結果を返すような処理をしているアプリの場合、WebViewを通じてアプリケーション固有のファイルを取得されてしまうことになります。

図2. にあてはめると、ロジック部がWebViewとなります。WebViewからファイルにアクセスしてその結果をアプリBに返すため、アプリBはアプリA固有のファイル内容を取得できてしまいます。

また、呼び出し元アプリに結果を戻すような実装になっていない場合でも、WebViewでJavaScriptを有効にしていると、悪意のあるJavaScriptを含むHTMLファイルをWebViewで読み込まされることによりファイル自体を取得され外部に送信される危険性もあります。

これについてもいくつか手法がありますが、一例をあげると以下図3のような仕組みになります。

  1. アプリBから悪意のあるJavaScriptを含むhtmlファイルをSDカードに保存
  2. アプリBからアプリAにアクセス先としてhtmlファイルを指定したIntentを送信
  3. アプリAのWebView内でhtmlファイルが読み込まれる
  4. html内のJavaScriptが実行され、アプリA固有のファイルを読み込まれる
  5. その内容が悪意のあるJavaScriptによりインターネット上にアップロードされる

図3.JavaScriptを使用したファイルの取得と外部送信の例

原因と対策

アクセス先として渡される文字列がアプリの動作仕様に適っているかどうかの確認やファイルパスの正規化をしていないことが脆弱性の原因になります。

「file://」スキームを使用しない場合は入力値を検証して使用するプロトコルを限定 (webアクセスだけであればプロトコルがhttpとhttps以外はエラーとする等) します。以下に実装例を示します。

String urlstring = xxx; //xxxはアクセス先として渡されてきた文字列)
try {
    URL url = new URL(urlstring);
    String protocol = url.getProtocol();

    if(! protocol.equals("http") && ! protocol.equals("https")){
            throw new MalformedURLException("protocol is not http or https");
     }

    //スキームがhttpかhttpsだった場合の正常処理

}catch (MalformedURLException e){
    e.printStackTrace();
}

「file://」スキームを使用する場合、ディレクトリトラバーサル攻撃を受けないようにすることが必要です。

あらかじめアクセスさせてもよいディレクトリを決めておける場合は、ファイルパスを正規化したうえでアクセス先が当該ディレクトリかどうかについて確認します。ファイルパスの正規化にはファイルパス中に親ディレクトリを表す「//」、「/./」、「/../」などの記述が存在する場合にそれを相対パスから絶対パスに変換します。
以下がパス変換の実装の具体例になります。

String inputPath = xxx; //xxxは入力値として渡ってきたファイルパス
//アクセスさせてよいディレクトリをあらかじめ決めておく
File targetDir = new File("/data/data/com.example.checkfilepath/files/");
try {
    //入力値のファイルの親ディレクトリ名を取得
    File inputDir = new File(inputPath).getCanonicalFile().getParentFile();
    if(! inputDir.equals(targetDir)){
        //例外処理
    }

    //入力値のファイルパスが目的のディレクトリ内に存在した場合の正常処理

} catch (IOException e) {
    e.printStackTrace();
}

WebViewでの入力データや履歴の保存について

WebViewには上記のような脆弱性があった場合に更に被害が大きくなる可能性がある仕様があります。WebViewが過去に使用した以下のような情報を平文でDBファイルに保存していることです。

  • 入力フォームでの入力情報
  • HTTP認証(Basic認証、Digest認証) 情報(※2)
  • 入力後に「保存」を選択した場合IDとパスワード情報
  • アクセスしたURL

(※2)WebViewでHTTP認証使用する場合は専用のロジックが必要ですが、認証情報をユーザに入力させずアプリに埋め込む方式も可能です。この場合ユーザが知らないうちに認証情報が保存されていることになります。

これらの情報は開発者の意図に関わらず自動的に作成されるwebview.dbやwebviewCache.dbというSQLite のDBファイルに保存されるため、保存されていること自体を知らずに利用している開発者も多いのではないかと思います。以下に実際にsqliteコマンドでwebview.db内の情報を表示した際の画面を図4に掲載します。

図4.DBファイルの中身

※検証用にエミュレータ上で表示しています

  1. /data/data/[AppName]/databases配下に二つのDBファイルが存在します。webview.db内にはフォームでの入力や認証情報、webviewCache.db内のデータはアクセス先URLの履歴が保存されています。
  2. webview.dbファイル内のテーブル一覧です。
    「formdata」テーブルにはPOSTメソッドで送信した内容が保存されています。クレジットカード情報等の機密情報を入力していても平文で保存されます。
  3. 「password」テーブル内に保存されているデータです。フォームで入力して保存したユーザ名とパスワードが平文で保存されています。
  4. 「httpauth」テーブル内の保存されているデータです。Basic認証およびDigest認証で入力したユーザ名とパスワードが平文で保存されています。

また、図4のようにこれらのファイルは/data/data/[APPNAME]/databases 配下という統一的なファイルパスで保存されており、ファイルにアクセスされやすくなる原因になっています( PC版のThunderbirdではメール等が保存されるフォルダの上位フォルダがランダムな文字列で構成されており、脆弱性があっても外部からアクセスしにくいようなつくりになっています)。

前述したデータ漏えいの脆弱性が存在すると、同時に上記のような認証情報等の機密情報も取得可能であるため被害が大きくなる可能性が高まります。
webview.dbに保存される情報が平文であること自体については現在のWebViewの仕様ですので、アプリケーション開発者側で対策をとることは難しいです。

機密情報等、保存されて困るような情報をWebViewで扱わないことを設計時から意識することが重要ですが、使用する場合には定期的にキャッシュ内容を削除する等の対策の実施もあわせて考えていただければと思います。

執筆者プロフィール 鈴木 崇文、安部 剛

NTT-CERTメンバとして、セキュリティインシデントの対応支援、再発防止策の 検討、トレーニングプログラムの開発およびセ キュリティ関連情報の提供など を実施中。