WebView - ネイティブ ブリッジ

OWASP カテゴリ: MASVS-PLATFORM: プラットフォームのインタラクション

概要

ネイティブ ブリッジ(JavaScript ブリッジとも呼ばれます)は、addJavascriptInterface メソッドを使用して WebView とネイティブ Android コード間の通信を容易にするメカニズムです。これにより、WebView で実行されている JavaScript コードと Android アプリケーションの Java コードの間で双方向通信が可能になります。addJavascriptInterface メソッドは、WebView のすべてのフレームに Java オブジェクトを公開します。どのフレームもオブジェクト名にアクセスして、メソッドを呼び出すことができます。ただし、WebView 内の呼び出し元フレームのオリジンを検証するメカニズムがアプリケーションにないため、コンテンツの信頼性が不明なままとなり、セキュリティ上の懸念が生じます。

ネイティブ ブリッジは、Android の WebViewCompat.postWebMessage または WebMessagePort.postMessage を使用して JavaScript の Window.postMessage と通信する HTML メッセージ チャネルで実装することもできます。WebViewCompat.postWebMessageWebMessagePort.postMessage は、WebView 内で実行される Window.postMessage を介して送信された JavaScript メッセージを受け入れることができます。

ネイティブ ブリッジには、次のような複数のリスクが伴います。

  • JavascriptInterface ベースのブリッジ:
    • addJavascriptInterface メソッドは、iframe を含む WebView のすべてのフレームに指定された Java オブジェクトを挿入します。つまり、悪意のある第三者が正規のウェブサイトにフレームを挿入して攻撃する可能性があります。このメソッドを使用すると JavaScript でホスト アプリケーションを制御できるため、API レベル 16 以前を対象とするアプリは特に攻撃を受けるリスクが高くなります。
    • ネイティブ ブリッジが有効な WebView で信頼できないユーザー提供のコンテンツを反映すると、クロスサイト スクリプティング(XSS)攻撃が可能になります。
  • MessageChannel ベースのブリッジ:
    • メッセージ チャネル エンドポイントでオリジン チェックが行われないため、悪意のあるコードを含むものを含め、すべての送信者からのメッセージが受け入れられます。
    • Java を任意の JavaScript に誤って公開する可能性があります。

影響

addJavascriptInterfacepostWebMessagepostMessage メソッドは、悪意のあるユーザーが WebView にアクセスして操作したり、制御するコードを挿入したりするために利用される可能性があります。これにより、ユーザーが悪意のあるサイトにリダイレクトされたり、悪意のあるコンテンツが読み込まれたり、デバイスで悪意のあるコードが実行されてセンシティブ データが抽出されたり、権限昇格が実現されたりする可能性があります。

リスク: addJavascriptInterface のリスク

WebView は、ページのレンダリング、ナビゲーション、JavaScript の実行など、ブラウザの基本的な機能を実装します。WebView は、アクティビティ レイアウトの一部としてウェブ コンテンツを表示するために、アプリ内で使用できます。addJavascriptInterface メソッドを使用して WebView 内にネイティブ ブリッジを実装すると、クロスサイト スクリプティング(XSS)などのセキュリティ上の問題が発生する可能性があります。また、攻撃者がインターフェース インジェクションを通じて信頼できないコンテンツを読み込み、ホスト アプリケーションを意図しない方法で操作し、ホスト アプリケーションの権限で Java コードを実行する可能性があります。

リスクの軽減

JavaScript を無効にする

WebView で JavaScript が必要ないシナリオでは、WebSettings 内で setJavaScriptEnabled を呼び出さないでください(静的 HTML コンテンツを表示している場合など)。デフォルトでは、WebView での JavaScript の実行は無効になっています。

信頼できないコンテンツを読み込むときに JavaScript インターフェースを削除

信頼できないコンテンツが WebView で読み込まれる前に、removeJavascriptInterface を呼び出して JavaScript インターフェースからオブジェクトが削除されるようにします。たとえば、shouldInterceptRequest の呼び出しで行うことができます。

Kotlin

webView.removeJavascriptInterface("myObject")

Java

webView.removeJavascriptInterface("myObject");

HTTPS 経由でのみウェブ コンテンツを読み込む

信頼できないコンテンツを読み込む必要がある場合は、WebView で暗号化された接続を介してウェブ コンテンツが読み込まれるようにしてください(クリアテキスト通信に関するガイドラインも参照してください)。AndroidManifest ファイルで android:usesCleartextTrafficfalse に設定するか、ネットワーク セキュリティ構成で HTTP トラフィックを禁止することで、暗号化されていない接続で初期ページ読み込みが行われるのを防ぎます。詳細については、usesCleartextTraffic のドキュメントをご覧ください。

XML

<application
    android:usesCleartextTraffic="false">
    <!-- Other application elements -->
</application>

リダイレクトやアプリのブラウジングが暗号化されていないトラフィックで行われないようにするには、loadUrl または shouldInterceptRequest で HTTP スキームを確認します。

Kotlin

fun loadSecureUrl(webView: WebView?, url: String?) {
    webView?.let { wv ->  // Ensure valid WebView and URL
        url?.let {
            try {
                val uri = URI(url)
                if (uri.scheme.equals("https", ignoreCase = true)) { // Enforce HTTPS scheme for security
                    wv.loadUrl(url)
                } else {
                    // Log an error or handle the case where the URL is not secure
                    System.err.println("Attempted to load a non-HTTPS URL: $url")
                }
            } catch (e: Exception) {
                // Handle exception for improper URL format
                System.err.println("Invalid URL syntax: $url")
            }
        }
    }
}

Java

public void loadSecureUrl(WebView webView, String url) {
    if (webView != null && url != null) { // Ensure valid WebView and URL
        try {
            URI uri = new URI(url);
            String scheme = uri.getScheme();
            if ("https".equalsIgnoreCase(scheme)) { // Enforce HTTPS scheme for security
                webView.loadUrl(url);
            } else {
                // Log an error or handle the case where the URL is not secure
                System.err.println("Attempted to load a non-HTTPS URL: " + url);
            }
        } catch (URISyntaxException e) {
            // Handle exception for improper URL format
            System.err.println("Invalid URL syntax: " + url);
        }
    }
}

信頼できないコンテンツを検証する

WebView で外部リンクが読み込まれた場合は、スキームとホスト(許可リストのドメイン)の両方を検証します。許可リストに含まれていないドメインは、代わりにデフォルトのブラウザで開く必要があります。

信頼できないコンテンツを読み込まない

可能な場合は、アプリ デベロッパーが所有する URL とコンテンツのみに厳密に範囲を絞って WebView に読み込まれるようにします。

センシティブ データを公開しない

アプリが WebView を使用してセンシティブ データにアクセスする場合は、JavaScript インターフェースを使用する前に、clearCache メソッドを使用して、ローカルに保存されているファイルをすべて削除することをおすすめします。また、no-store などのサーバーサイド ヘッダーを使用して、アプリが特定のコンテンツをキャッシュすることのないように指示します。

機密性の高い機能を公開しない

アプリで機密情報に関わる権限が必要な場合や、センシティブ データを収集する場合は、アプリ内のコードから呼び出されるようにし、ユーザーに認識しやすい開示を提供するようにしてください。機密性の高いオペレーションやユーザーデータには JavaScript インターフェースを使用しないでください。

対象 API レベル 21 以上

addJavascriptInterface メソッドを安全に使用する 1 つの方法は、対象 API レベル 21 以上をターゲットとし、API レベル 21 以上で実行されている場合にのみメソッドが呼び出されるようにすることです。API 21 より前は、JavaScript でリフレクションを使用して、挿入されたオブジェクトの公開フィールドにアクセスできました。


リスク: MessageChannel のリスク

postWebMessage()postMessage() でのオリジン制御の欠如により、攻撃者がメッセージを傍受したり、ネイティブ ハンドラにメッセージを送信したりする可能性があります。

リスクの軽減

postWebMessage() または postMessage() を設定する際は、ターゲット オリジンとして * を使用せず、代わりに想定される送信ドメインを明示的に指定することで、信頼できるドメインからのメッセージのみを許可します。


リソース