OWASP category: MASVS-STORAGE: Storage
Overview
This document covers several issues related to file inclusion that share similar
mitigations. These issues center on vulnerabilities stemming from access to
files within WebViews and range from dangerous WebSettings
allowing file
access, or enabling JavaScript, to a WebKit method that creates a file selection
request. This document should be helpful if you are looking for guidance on
remediation of issues within WebView arising from the use of the file://
scheme, unrestricted access to local files, and cross-site scripting.
More concretely, this document covers the following topics:
WebSettings
is a class containing methods which manage the setting states for WebViews. These methods can open WebViews to different attacks which will be outlined later. In this document we will look at the methods that pertain to how files can be accessed, and the setting that allows JavaScript to be executed:- The
setAllowFileAccess
,setAllowFileAccessFromFileURLs
, andsetAllowUniversalAccessFromFileURLs
methods can be used to grant access to local files, using a file scheme URL (file://
). However, they can be exploited by malicious scripts to access arbitrary local files that the application has access to, such as their own/data/
folder. For this reason, these methods have been flagged as insecure and were deprecated in API 30 in favor of safer alternatives, such asWebViewAssetLoader
. - The
setJavascriptEnabled
method can be used to enable the execution of JavaScript within WebViews. This leaves applications vulnerable to file-based XSS. Especially when configured to allow loading of local files or untrusted web content which may contain executable code, configured to allow access to files that can be created or changed by external sources, or allowing WebViews to execute JavaScript, users and their data are put at risk. WebChromeClient.onShowFileChooser
is a method belonging to theandroid.webkit
package, which provides web browsing tools. This method can be used to allow users to select files within a WebView. However, this feature can be abused because WebViews don't enforce restrictions on which file is selected.
Impact
The impact of file inclusion can depend on which WebSettings are configured in
WebView. Overly broad file permissions can allow attackers to access local files
and steal sensitive data, PII (Personally Identifiable Information), or private
app data. Enabling JavaScript execution can allow attackers to run JavaScript
within a WebView or on a user's device. Files selected using the
onShowFileChooser
method could compromise user security since there is no way
for the method or WebView to ensure that the file source is trusted.
Risk: Risky Access to Files through file://
Enabling setAllowFileAccess
, setAllowFileAccessFromFileURLs
, and
setAllowUniversalAccessFromFileURLs
can allow malicious intents and WebView
requests with a file://
context to access arbitrary local files, including
WebView cookies and app private data. Further, using the onShowFileChooser
method can allow users to select and download files from untrusted sources.
These methods can all lead to the exfiltration of PII, login credentials, or other sensitive data, depending on the application configuration.
Mitigations
Validate file URLs
If your app requires access to files through file://
URLs, it is important to
allowlist only specific URLs that are known to be legitimate, avoiding common
mistakes.
Use WebViewAssetLoader
Use WebViewAssetLoader
instead of the mentioned methods. This method uses
the http(s)//:
scheme instead of a file://
scheme to access local file
system assets and is not vulnerable to the described attack.
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");
Disable dangerous WebSettings methods
The values of the methods setAllowFileAccess()
,
setAllowFileAccessFromFileURLs()
, and setAllowUniversalAccessFromFileURLs()
are by default set to TRUE
in API level 29 and lower, and FALSE
in API level
30 and higher.
If there is a need to configure other WebSettings
, it would be best to
explicitly disable these methods, especially for apps targeting API levels
less than or equal to 29.
Risk: File-Based XSS
Setting the setJavacriptEnabled
method to TRUE
allows JavaScript to be
executed within a WebView, and in combination with file access enabled as
outlined earlier, file-based XSS is possible through execution of code within
arbitrary files, or malicious websites opened within the WebView.
Mitigations
Prevent WebViews from loading local files
As with the previous risk, file-based XSS can be avoided if
setAllowFileAccess()
, setAllowFileAccessFromFileURLs()
, and
setAllowUniversalAccessFromFileURLs()
are set to FALSE
.
Prevent WebViews executing JavaScript
Set the method setJavascriptEnabled
to FALSE
so that JavaScript can't be
executed within WebViews.
Ensure WebViews don't load untrusted content
Sometimes enabling these settings is necessary within WebViews. In this case, it
is important to ensure that only trusted content is loaded. Limiting the
execution of JavaScript to only that which you control and disallowing arbitrary
JavaScript is one good way to ensure content is trustworthy. Otherwise,
preventing cleartext traffic from being loaded ensures that WebViews with
dangerous settings are at least not able to load HTTP URLs. This can be done
either through the manifest, setting android:usesCleartextTraffic
to
False
, or by setting a Network Security Config
that disallows HTTP
traffic.
Resources
- setAllowUniversalAccessFromFileURLs API reference page
- setAllowFileAccessFromFileURLs API reference page
- WebViewAssetLoader API reference page
- CodeQL documentation
- Oversecured blog
- onShowFileChooser reference page