OWASP 类别: MASVS-CODE: 代码质量
概览
当 Android 应用未能正确评估 URI 的有效性便将其加载到 WebView 时,就会发生不安全的 URI 加载。
此类漏洞的根本原因在于,URI 由多个部分组成,其中,方案和主机(权限部分)必须在 URI 加载到 WebView 或被应用内部使用之前得到验证(例如,加入白名单)。
最常见的错误包括
- 仅检查主机但不检查方案,导致攻击者可以使用
http://
、content://
或javascript://
等方案访问已验证的主机。 - 未能正确解析 URI,尤其是在 URI 作为字符串接收的情况下。
- 验证方案但主机验证不足。
关于最后一种情况,这通常发生在应用需要允许主要域的任意子域时。因此,即使已正确提取主机名,应用仍使用 java.lang.String
类的 startsWith
、endsWith
或 contains
等方法来验证提取的字符串部分中是否存在主要域。如果使用不当,这些方法可能导致错误的结果,并迫使应用不当地信任可能恶意的主机。
影响
根据主机的使用环境,影响可能会有所不同。在将恶意 URI(即绕过过滤/白名单的 URI)加载到 WebView 的情况下,可能导致账户被盗(例如,使用网络钓鱼)、代码执行(例如,加载恶意 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
需要在域名之前包含“.
”点字符才能准确匹配)。忽略这些字符可能导致不准确的匹配并损害安全性。由于子域可以无限嵌套,因此不建议使用正则表达式匹配作为验证主机名的策略。
贡献者:Microsoft 威胁情报部的 Dimitrios Valsamaras 和 Michael Peck