Granting file access to WebViews, particularly through the file:// scheme, can expose sensitive local files to malicious scripts.

Why is this an issue?

WebViews can be configured to allow scripts to access local files through the file:// scheme. When file access settings are enabled, JavaScript running within the WebView can read files from the device’s local file system, including sensitive application data.

Settings such as setAllowFileAccess, setAllowContentAccess, allowFileAccessFromFileURLs, and allowUniversalAccessFromFileURLs control this behavior on Android.

What is the potential impact?

Data exposure

If a malicious script is loaded in a WebView with file access enabled, it can read arbitrary files from the device’s local file system. This includes sensitive application data, user credentials, authentication tokens, and Personally Identifiable Information (PII) stored in local files.

Loss of confidentiality

Exposed data can be exfiltrated to attacker-controlled servers, leading to account compromise, identity theft, and data breaches.

How to fix it

Use WebViewAssetLoader to load local files instead of directly accessing them via file:// URLs. This approach serves assets over a secure https://appassets.androidplatform.net URL, effectively isolating the WebView from the local file system.

The file access settings are disabled by default in modern Android versions. To prevent possible security issues in Build.VERSION_CODES.Q and earlier, it is still recommended to explicitly set those values to false.

Code examples

Noncompliant code example

AndroidView(
    factory = { context ->
        WebView(context).apply {
            webViewClient = WebViewClient()
            settings.apply {
                allowFileAccess = true  // Noncompliant
                allowFileAccessFromFileURLs = true  // Noncompliant
                allowUniversalAccessFromFileURLs = true  // Noncompliant
                allowContentAccess = true  // Noncompliant
            }
            loadUrl("file:///android_asset/example.html")
        }
   }
)

Compliant solution

AndroidView(
    factory = { context ->
        val webView = WebView(context)
        val assetLoader = WebViewAssetLoader.Builder()
            .addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(context))
            .build()

        webView.webViewClient = object : WebViewClient() {
            @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
            override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse? {
                return assetLoader.shouldInterceptRequest(request.url)
            }

            @Suppress("deprecation")
            override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
                return assetLoader.shouldInterceptRequest(Uri.parse(url))
            }
        }

        webView.settings.apply {
            allowFileAccess = false
            allowFileAccessFromFileURLs = false
            allowUniversalAccessFromFileURLs = false
            allowContentAccess = false
        }

        webView.loadUrl("https://appassets.androidplatform.net/assets/example.html")
        webView
    }
)

Resources

Documentation

Standards