OWASP 类别: MASVS-CODE:代码质量
概述
当 Android 应用在将 URI 加载到 WebView 之前未能正确评估 URI 的有效性时,就会发生不安全的 URI 加载。
此类漏洞背后的根本原因是 URI 由多个部分组成,其中至少必须验证方案和主机(授权部分的主机)(例如,允许列表)才能将 URI 加载到 WebView 或应用内部使用。
最常见的错误包括
- 检查主机但不检查方案,允许攻击者使用
http://
、content://
或javascript://
等方案以及经过身份验证的主机。 - 无法正确解析 URI,尤其是在 URI 作为字符串接收的情况下。
- 验证方案但不验证主机(主机验证不足)。
关于最后一种情况,通常发生在应用需要允许主域的任意子域时。因此,即使已正确提取主机名,应用也会使用 startsWith
、endsWith
或 contains
等 java.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 部分后,务必对其进行完整验证(而不是部分验证),以准确识别主机是否可信。当无法避免使用 startsWith
或 endsWith
等方法时,务必使用正确的语法,并且不要忽略必要的字符或符号(例如,endsWith
要求在域名之前使用“.
”点字符才能进行准确匹配)。忽略这些字符可能会导致匹配不准确并危及安全性。由于子域名可以无限嵌套,因此正则表达式匹配不是验证主机名的推荐策略。
贡献者:微软威胁情报的 Dimitrios Valsamaras 和 Michael Peck