WebView - 不安全的 URI 載入情形
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
OWASP 類別:MASVS-CODE:程式碼品質
總覽
如果 Android 應用程式無法在將 URI 載入 WebView 前正確評估其有效性,就會發生不安全的 URI 載入情形。
這類安全漏洞背後的根本原因是 URI 內含多個部分;在 URI 載入 WebView 或由應用程式在內部使用時,至少要針對其中的配置和主機 (主機名稱部分) 完成驗證 (例如加入許可清單)。
最常見的錯誤包括:
- 檢查主機而非配置,以致於讓攻擊者透過經過驗證的主機使用
http://
、content://
或 javascript://
等配置。
- 無法正確剖析 URI,尤其是收到 URI 做為字串時。
- 驗證配置而非主機 (主機驗證不足)。
以最後一個案例來說,如果應用程式需要允許主網域內的任意子網域,通常就會發生這種情況。因此,即使主機名稱已正確擷取,應用程式還是會使用 java.lang.String
類別的 startsWith
、endsWith,
或 contains
等方法,驗證擷取的字串區段是否存在主網域。萬一使用不當,這些方法可能會導致錯誤結果,並強制應用程式誤信潛在的惡意主機。
影響
影響可能因主機的使用情境而有所不同。如果系統在 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
需要在網域名稱前加上「.
」點號字元才能正確比對)。省略這些字元可能會導致比對錯誤,並危及安全性。由於子網域可以建立無限的巢狀結構,因此比對規則運算式不是驗證主機名稱時建議採用的策略。
資源
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2024-01-05 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2024-01-05 (世界標準時間)。"],[],[],null,["# Webviews – Unsafe URI Loading\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-CODE: Code Quality](https://mas.owasp.org/MASVS/10-MASVS-CODE)\n\n\nOverview\n--------\n\nAn unsafe URI Loading occurs when an Android application fails to correctly\nevaluate the validity of a URI before loading it into a WebView.\n\nThe underlying reason behind this type of vulnerability is that a\n[URI consists of multiple parts](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/net/URI.html), of which, at a minimum, the scheme and the\nhost (of the authority part) must be verified (e.g. allowlisted) before the URI\ngets loaded to a WebView or is used internally by the application.\n\nThe most common mistakes include:\n\n- Checking the host but not the scheme, allowing an attacker to use schemes like `http://`, `content://` or `javascript://` with an authenticated host.\n- Failing to parse the URI correctly, especially in cases where the URI is received as a string.\n- Validating the scheme but not the host (insufficient host validation).\n\nRegarding the last case, this usually occurs when the application needs to allow\narbitrary subdomains of a primary domain. So, even if the hostname has been\nextracted correctly, the app uses methods such as `startsWith`, `endsWith,` or\n`contains` of the `java.lang.String` class to validate the presence of a primary\ndomain in the extracted string section. Used incorrectly, these methods may lead\nto faulty results and force the application to improperly trust a potentially\nmalicious host.\n\nImpact\n------\n\nDepending on the context in which the host is used, the impact can vary. In\ncases where loading a malicious URI (i.e., one that bypassed\nfiltering/allowlist) in a WebView could potentially lead to account takeover\n(e.g. using phishing), code execution (e.g., loading malicious JavaScript), or\ndevice compromise (exploit code delivered using hyperlink).\n\nMitigations\n-----------\n\nWhen handling string URIs, it is important to parse the string as a URI and\nvalidate both the scheme and the host: \n\n### Kotlin\n\n fun isUriTrusted(incomingUri: String, trustedHostName: String): Boolean {\n try {\n val uri = Uri.parse(incomingUri)\n return uri.scheme == \"https\" && uri.host == trustedHostName\n } catch (e: NullPointerException) {\n throw NullPointerException(\"incomingUri is null or not well-formed\")\n }\n }\n\n### Java\n\n public static boolean isUriTrusted(String incomingUri, String trustedHostName)\n throws NullPointerException {\n try {\n Uri uri = Uri.parse(incomingUri);\n return uri.getScheme().equals(\"https\") &&\n uri.getHost().equals(trustedHostName);\n } catch (NullPointerException e) {\n throw new NullPointerException(\n \"incomingUri is null or not well-formed\");\n }\n }\n\nFor host validation, after isolating the corresponding URI part, it is important\nto validate it entirely (rather than partially) to accurately identify whether\nthe host is trusted or not. When using methods like `startsWith` or `endsWith`\ncan't be avoided, it is important to use the correct syntax and not overlook\nnecessary characters or symbols (for example, `endsWith` requires the \"`.`\" dot\ncharacter before the domain name for an accurate match). Neglecting these\ncharacters may lead to inaccurate matches and compromise security. Since\nsubdomains can be infinitely nested, regular expression matching is not a\nrecommended strategy for validating hostnames.\n\nContributors: Dimitrios Valsamaras and Michael Peck of Microsoft Threat\nIntelligence\n\nResources\n---------\n\n- [getHost() documentation](/reference/java/net/URI#getHost())\n- [getScheme() documentation](/reference/java/net/URI#getScheme())"]]