WebView - 原生桥接

OWASP 类别: MASVS-PLATFORM: 平台交互

概览

原生桥接(有时也称为 JavaScript 桥接)是一种通过使用 addJavascriptInterface 方法实现 WebView 和原生 Android 代码之间通信的机制。这允许在 WebView 中运行的 JavaScript 代码与 Android 应用的 Java 代码之间进行双向通信。addJavascriptInterface 方法将一个 Java 对象暴露给 WebView 的所有帧,任何帧都可以访问该对象名称并调用其方法。但是,应用无法验证 WebView 内调用帧的来源,这会引发安全问题,因为内容的信任度是不确定的。

原生桥接也可以使用 Android 的 WebViewCompat.postWebMessageWebMessagePort.postMessage 通过 HTML 消息通道实现,与 JavaScript Window.postMessage 进行通信。WebViewCompat.postWebMessageWebMessagePort.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) 攻击。
  • 基于 MessageChannel 的桥接
    • 消息通道端点缺少来源检查,这意味着会接受来自任何发送方的消息,包括包含恶意代码的消息。
    • 有可能意外地将 Java 暴露给任意 JavaScript。

影响

恶意行为者可以利用 addJavascriptInterfacepostWebMessagepostMessage 方法访问、操纵或将他们控制的代码注入到 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>

为了确保重定向和后续应用浏览不会在未加密流量上发生,请在 loadUrlshouldInterceptRequest 中检查 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() 时,避免使用 * 作为目标来源,而是明确指定预期的发送域名,从而只允许来自受信任域名的消息。


资源