WebView – 不安全 URI 加载

OWASP 类别: MASVS-CODE:代码质量

概述

当 Android 应用在将 URI 加载到 WebView 之前未能正确评估 URI 的有效性时,就会发生不安全的 URI 加载。

此类漏洞背后的根本原因是 URI 由多个部分组成,其中至少必须验证方案和主机(授权部分的主机)(例如,允许列表)才能将 URI 加载到 WebView 或应用内部使用。

最常见的错误包括

  • 检查主机但不检查方案,允许攻击者使用 http://content://javascript:// 等方案以及经过身份验证的主机。
  • 无法正确解析 URI,尤其是在 URI 作为字符串接收的情况下。
  • 验证方案但不验证主机(主机验证不足)。

关于最后一种情况,通常发生在应用需要允许主域的任意子域时。因此,即使已正确提取主机名,应用也会使用 startsWithendsWithcontainsjava.lang.String 类的方法来验证提取的字符串部分中是否存在主域。如果使用不当,这些方法可能会导致错误的结果,并强制应用错误地信任潜在的恶意主机。

影响

根据主机使用环境的不同,影响也会有所不同。在 WebView 中加载恶意 URI(即绕过过滤/允许列表的 URI)可能导致帐户接管(例如,使用网络钓鱼)、代码执行(例如,加载恶意 JavaScript)或设备泄露(使用超链接传递漏洞利用代码)的情况下。

缓解措施

处理字符串 URI 时,务必将字符串解析为 URI 并验证方案和主机。

Kotlin

fun isUriTrusted(incomingUri: String, trustedHostName: String): Boolean {
    try {
        val uri = Uri.parse(incomingUri)
        return uri.scheme == "https" && uri.host == trustedHostName
    } catch (e: NullPointerException) {
        throw NullPointerException("incomingUri is null or not well-formed")
    }
}

Java

public static boolean isUriTrusted(String incomingUri, String trustedHostName)
    throws NullPointerException {
        try {
            Uri uri = Uri.parse(incomingUri);
            return uri.getScheme().equals("https") &&
            uri.getHost().equals(trustedHostName);
        } catch (NullPointerException e) {
            throw new NullPointerException(
                "incomingUri is null or not well-formed");
        }
    }

为了进行主机验证,在隔离相应 URI 部分后,务必对其进行完整验证(而不是部分验证),以准确识别主机是否可信。当无法避免使用 startsWithendsWith 等方法时,务必使用正确的语法,并且不要忽略必要的字符或符号(例如,endsWith 要求在域名之前使用“.”点字符才能进行准确匹配)。忽略这些字符可能会导致匹配不准确并危及安全性。由于子域名可以无限嵌套,因此正则表达式匹配不是验证主机名的推荐策略。

贡献者:微软威胁情报的 Dimitrios Valsamaras 和 Michael Peck

资源