OWASP 類別:MASVS-PLATFORM:平台互動
總覽
原生橋接 (有時稱為 JavaScript 橋接) 是一種機制,可透過使用 addJavascriptInterface
方法,簡化 WebView 和原生 Android 程式碼之間的通訊。這樣一來
在 WebView 和 Android 中執行的 JavaScript 程式碼
執行應用程式的 Java 程式碼addJavascriptInterface
方法會將 Java 物件公開給 WebView 的所有影格,任何影格都可以存取物件名稱,並在其上呼叫方法。不過,應用程式沒有任何機制可驗證 WebView 中呼叫框架的來源,因此內容的可信度仍不明確,因此會引發安全性疑慮。
您也可以透過使用
Android 的 WebViewCompat.postWebMessage
WebMessagePort.postMessage
:用來與 JavaScript 通訊
Window.postMessage
。WebViewCompat.postWebMessage
和 WebMessagePort.postMessage
可接受透過 Window.postMessage
傳送的 JavaScript 訊息,這些訊息會在 WebView 中執行。
原生橋接程式碼有以下幾項風險:
- 以 JavascriptInterface 為基礎的橋接:
addJavascriptInterface
方法會將提供的 Java 物件插入 WebView 的每個框架 (包括 iframe),這表示該方法容易遭到惡意第三方攻擊,導致攻擊者將框架插入合法網站。以 API 級別 16 以下為目標的應用程式特別容易遭到攻擊,因為這個方法可讓 JavaScript 控制主機應用程式。- 在支援原生橋接的 WebView 中顯示不受信任的使用者提供內容,可能會導致跨網站指令碼攻擊 (XSS)。
- MessageChannel 型橋接:
- 對訊息通道端點缺乏來源檢查表示訊息 接受任何寄件者 (包括含有惡意程式碼的寄件者) 的同意。
- 可能會不小心將 Java 暴露給任意 JavaScript。
影響
addJavascriptInterface
、postWebMessage
和 postMessage
方法可
利用惡意行為人存取、操控或插入他們掌控的程式碼
轉換成 WebView這可能會導致使用者被重新導向至惡意網站
載入惡意內容,或惡意程式碼的裝置上執行
擷取機密資料或實現權限提升
風險:addJavascriptInterface 風險
WebView 會實作瀏覽器的基本功能,例如網頁轉譯、導覽和 JavaScript 執行。WebView 可在應用程式中使用
在活動版面配置中顯示網路內容。使用 addJavascriptInterface
方法在 WebView 中實作原生橋接,可能會造成跨網站指令碼攻擊 (XSS) 等安全性問題,或是讓攻擊者透過介面注入來載入不受信任的內容,並以非預期的方式操控主機應用程式,執行主機應用程式的權限 Java 程式碼。
因應措施
停用 JavaScript
如果 WebView 不需要 JavaScript,請勿在 WebSettings
中呼叫 setJavaScriptEnabled
(例如在顯示靜態 HTML 內容時)。根據預設,WebView 會停用 JavaScript 執行作業。
在載入不受信任的內容時移除 JavaScript 介面
呼叫
removeJavascriptInterface
未受信任的內容由
WebView。例如,您可以在呼叫 shouldInterceptRequest
時執行此操作。
Kotlin
webView.removeJavascriptInterface("myObject")
Java
webView.removeJavascriptInterface("myObject");
僅透過 HTTPS 載入網路內容
如需載入不受信任的內容,請確認 WebView 會透過
加密連線 (另請參閱明文規範
Communications)。在 AndroidManifest
檔案中將 android:usesCleartextTraffic
設為 false
,或在網路安全性設定中禁止 HTTP 流量,即可避免在未加密的連線上執行初始網頁載入作業。詳情請參閱 usesCleartextTraffic
說明文件。
Xml
<application
android:usesCleartextTraffic="false">
<!-- Other application elements -->
</application>
避免在未加密的情況下重新導向,並繼續瀏覽應用程式
流量,請檢查 loadUrl
中的 HTTP 配置,或
shouldInterceptRequest
:
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 中載入,請驗證配置和主機 (將網域加入許可清單)。任何不在許可清單中的網域,都應改由預設瀏覽器開啟。
不要載入不受信任的內容
盡可能只將嚴格限定範圍的網址和應用程式開發人員擁有的內容載入 WebView。
避免提供私密資料
如果您的應用程式會透過 WebView 存取機密資料,建議您使用
clearCache
方法,先刪除儲存在本機的所有檔案,再使用
JavaScript 介面。您也可以使用伺服器端標頭 (例如 no-store),指出應用程式不應快取特定內容。
不要公開機密功能
如果您的應用程式需要機密權限或收集機密資料, 確保應用程式內的程式碼呼叫這個方法,且最醒目 揭露資訊會向使用者提交。避免使用 JavaScript 介面 敏感作業或使用者資料
目標 API 級別 21 以上
以安全的方式使用 addJavascriptInterface
方法,就是指定 API 級別
21 以上版本,確保只有在 API 級別 21 執行時才會呼叫方法
或更高版本。在 API 21 之前,JavaScript 可以使用反射來存取已插入物件的公開欄位。
風險:MessageChannel 風險
如果 postWebMessage()
和 postMessage()
缺乏來源控管機制,攻擊者可能會攔截訊息,或將訊息傳送至原生處理常式。
因應措施
設定 postWebMessage()
或 postMessage()
時,請避免使用 * 做為目標來源,改為明確指定預期的傳送網域,這樣一來,系統就只會允許來自信任網域的郵件。
資源
- postMessage() 最佳做法
- addJavascriptInterface 說明文件
- postMessage() 說明文件
- WebMessagePort.postMessage() 說明文件
- WebViewClient.shouldInterceptRequest 說明文件
- 關於 addJavaScriptInterface 安全性建議的說明文件
- clearCache 說明文件
- 移除 JavaScript 說明文件
- 在 WebView 中啟用 JavaScript