OWASP 類別:MASVS-STORAGE:儲存空間
總覽
本文將說明幾個與檔案納入作業相關的問題,這些問題有類似的緩解方法。這些問題主要與 WebView 內檔案存取權相關的安全漏洞有關,從允許檔案存取權或啟用 JavaScript 的危險 WebSettings
,到建立檔案選取要求的 WebKit 方法皆是安全漏洞。如果您想瞭解如何解決因使用 file://
配置、本機檔案的無限制存取權和跨網站指令碼攻擊,而導致 WebView 發生問題的問題,這份文件應該會對您有所幫助。
具體來說,本文件涵蓋以下主題:
WebSettings
是包含管理 WebView 設定狀態方法的類別。這些方法可能會讓 WebView 遭受各種攻擊,我們會在後文說明。在本文件中,我們將探討與檔案存取方式相關的方法,以及允許執行 JavaScript 的設定:setAllowFileAccess
、setAllowFileAccessFromFileURLs
和setAllowUniversalAccessFromFileURLs
方法可用於使用檔案配置網址 (file://
) 授予本機檔案存取權。不過,惡意指令碼可能會利用這些方法,存取應用程式可存取的任意本機檔案,例如自己的/data/
資料夾。因此,這些方法已標示為不安全,並在 API 30 中淘汰,改用更安全的替代方法,例如WebViewAssetLoader
。- 您可以使用
setJavascriptEnabled
方法,在 WebView 中啟用 JavaScript 執行功能。這會使應用程式容易受到檔案型 XSS 攻擊。尤其是在允許載入本機檔案或不安全的網路內容 (可能含有可執行程式碼)、允許存取外部來源可建立或變更的檔案,或允許 WebView 執行 JavaScript 的情況下,使用者和他們的資料就會面臨風險。 WebChromeClient.onShowFileChooser
是屬於android.webkit
套件的一種方法,可提供網路瀏覽工具。這個方法可讓使用者在 WebView 中選取檔案。不過,由於 WebView 不會對所選檔案強制執行限制,因此這項功能可能會遭到濫用。
影響
檔案加入作業的影響取決於 WebView 中設定的 WebSettings。過度寬鬆的檔案權限可能會讓攻擊者存取本機檔案,並竊取機密資料、個人識別資訊 (PII) 或私人應用程式資料。啟用 JavaScript 執行功能可能會讓攻擊者在 WebView 中或在使用者的裝置上執行 JavaScript。使用 onShowFileChooser
方法選取的檔案可能會危害使用者安全,因為該方法或 WebView 無法確保檔案來源可信。
風險:透過 file:// 存取檔案的風險
啟用 setAllowFileAccess
、setAllowFileAccessFromFileURLs
和 setAllowUniversalAccessFromFileURLs
可能會讓具有 file://
內容的惡意意圖和 WebView 要求存取任意本機檔案,包括 WebView Cookie 和應用程式私人資料。此外,使用 onShowFileChooser
方法可讓使用者從不受信任的來源選取及下載檔案。
這些方法都可能導致 PII、登入憑證或其他機密資料外洩,具體取決於應用程式設定。
因應措施
驗證檔案網址
如果您的應用程式需要透過 file://
網址存取檔案,請務必只將已知合法的特定網址加入許可清單,避免發生常見錯誤。
使用 WebViewAssetLoader
請改用 WebViewAssetLoader
,而非上述方法。這個方法使用 http(s)//:
配置,而非 file://
配置來存取本機檔案系統資產,因此不會受到上述攻擊的影響。
Kotlin
val assetLoader: WebViewAssetLoader = Builder()
.addPathHandler("/assets/", AssetsPathHandler(this))
.build()
webView.setWebViewClient(object : WebViewClientCompat() {
@RequiresApi(21)
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse {
return assetLoader.shouldInterceptRequest(request.url)
}
@Suppress("deprecation") // for API < 21
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse {
return assetLoader.shouldInterceptRequest(Uri.parse(url))
}
})
val webViewSettings: WebSettings = webView.getSettings()
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.allowFileAccessFromFileURLs = false
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.allowUniversalAccessFromFileURLs = false
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.allowFileAccess = false
webViewSettings.allowContentAccess = false
// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webView.loadUrl("https://appassets.androidplatform.net/assets/www/index.html")
Java
final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/assets/", new AssetsPathHandler(this))
.build();
webView.setWebViewClient(new WebViewClientCompat() {
@Override
@RequiresApi(21)
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
@Override
@SuppressWarnings("deprecation") // for API < 21
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return assetLoader.shouldInterceptRequest(Uri.parse(url));
}
});
WebSettings webViewSettings = webView.getSettings();
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.setAllowFileAccessFromFileURLs(false);
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.setAllowUniversalAccessFromFileURLs(false);
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.setAllowFileAccess(false);
webViewSettings.setAllowContentAccess(false);
// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webview.loadUrl("https://appassets.androidplatform.net/assets/www/index.html");
停用危險的 WebSettings 方法
在 API 級別 29 以下,方法 setAllowFileAccess()
、setAllowFileAccessFromFileURLs()
和 setAllowUniversalAccessFromFileURLs()
的值預設為 TRUE
;在 API 級別 30 以上,則為 FALSE
。
如果需要設定其他 WebSettings
,建議您明確停用這些方法,尤其是針對 API 級別小於或等於 29 的應用程式。
風險:檔案型跨網站指令碼攻擊
將 setJavacriptEnabled
方法設為 TRUE
可讓 JavaScript 在 WebView 中執行,並結合先前所述的檔案存取權,透過執行任意檔案中的程式碼,或在 WebView 中開啟的惡意網站,執行檔案型 XSS。
因應措施
禁止 WebView 載入本機檔案
與先前的風險相同,如果將 setAllowFileAccess()
、setAllowFileAccessFromFileURLs()
和 setAllowUniversalAccessFromFileURLs()
設為 FALSE
,就可以避免檔案型跨網站指令碼攻擊。
禁止 WebView 執行 JavaScript
將方法 setJavascriptEnabled
設為 FALSE
,這樣 JavaScript 就無法在 WebView 中執行。
確保 WebView 不會載入不受信任的內容
有時您必須在 WebView 中啟用這些設定。在這種情況下,請務必確保只載入可信任的內容。限制 JavaScript 執行作業,只執行您控制的作業,並禁止任意 JavaScript,是確保內容可信賴的好方法。否則,禁止載入明文流量可確保有危險設定的 WebView 至少無法載入 HTTP 網址。您可以透過資訊清單,將 android:usesCleartextTraffic
設為 False
,或是將 Network Security Config
設為不允許 HTTP 流量,來執行這項操作。
資源
- setAllowUniversalAccessFromFileURLs API 參考頁面
- setAllowFileAccessFromFileURLs API 參考頁面
- WebViewAssetLoader API 參考資料頁面
- CodeQL 說明文件
- Oversecured 網誌
- onShowFileChooser 參考頁面