OWASP 类别: MASVS-PLATFORM: 平台交互
概览
原生桥接(有时也称为 JavaScript 桥接)是一种通过使用 addJavascriptInterface
方法实现 WebView 和原生 Android 代码之间通信的机制。这允许在 WebView 中运行的 JavaScript 代码与 Android 应用的 Java 代码之间进行双向通信。addJavascriptInterface
方法将一个 Java 对象暴露给 WebView 的所有帧,任何帧都可以访问该对象名称并调用其方法。但是,应用无法验证 WebView 内调用帧的来源,这会引发安全问题,因为内容的信任度是不确定的。
原生桥接也可以使用 Android 的 WebViewCompat.postWebMessage
或 WebMessagePort.postMessage
通过 HTML 消息通道实现,与 JavaScript Window.postMessage
进行通信。WebViewCompat.postWebMessage
和 WebMessagePort.postMessage
可以接受通过 Window.postMessage
发送的 JavaScript 消息,这些消息将在 WebView 中执行。
原生桥接存在多种风险
- 基于 JavascriptInterface 的桥接
- The
addJavascriptInterface
method injects a supplied Java object into every frame of the WebView, including iframes, which means it is susceptible to attack by malicious third parties injecting frames into a legitimate website. Applications targeting API level 16 or earlier are particularly at risk of attack because this method can be used to allow JavaScript to control the host application. - 在启用了原生桥接的 WebView 中反映不受信任的用户提供的内容,可能导致跨站脚本 (XSS) 攻击。
- The
- 基于 MessageChannel 的桥接
- 消息通道端点缺少来源检查,这意味着会接受来自任何发送方的消息,包括包含恶意代码的消息。
- 有可能意外地将 Java 暴露给任意 JavaScript。
影响
恶意行为者可以利用 addJavascriptInterface
、postWebMessage
和 postMessage
方法访问、操纵或将他们控制的代码注入到 WebView 中。这可能导致用户被重定向到恶意网站、加载恶意内容,或在设备上运行可以提取敏感数据或实现权限提升的恶意代码。
风险:addJavascriptInterface 风险
WebView 实现了浏览器的基本功能,例如页面渲染、导航和 JavaScript 执行。WebView 可以在应用内部使用,将网页内容显示为 activity 布局的一部分。使用 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:usesCleartextTraffic
设置为 false
,或者在网络安全配置中禁止 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 中加载任何外部链接,请同时验证方案和主机(允许列表域名)。不在允许列表中的任何域名都应改用默认浏览器打开。
不要加载不受信任的内容
如果可能,仅将范围严格受限的网址和应用开发者拥有的内容加载到 WebView 中。
不要暴露敏感数据
如果您的应用使用 WebView 访问敏感数据,请考虑在使用 JavaScript 接口之前使用 clearCache
方法删除所有本地存储的文件。您还可以使用服务器端标头(例如 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 文档
- removeJavascript 文档
- 在 WebView 中启用 JavaScript