<br />

**OWASP category:** [MASVS-PLATFORM: Platform Interaction](https://mas.owasp.org/MASVS/09-MASVS-PLATFORM)

## Overview

A native bridge, sometimes known as a JavaScript bridge, is a mechanism that
facilitates communication between a WebView and native Android code, achieved by
using the [`addJavascriptInterface`](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String)) method. This allows for two-way
communication between JavaScript code running in the WebView and the Android
application's Java code. The `addJavascriptInterface` method exposes a Java
object to all of a WebView's frames, and any frame can access the object name
and call methods on it. However, there is no mechanism for the application to
verify the origin of the calling frame within the WebView, which raises security
concerns as the trustworthiness of the content remains indeterminate.

A native bridge can also be implemented with HTML message channels using
Android's [`WebViewCompat.postWebMessage`](https://developer.android.com/reference/androidx/webkit/WebViewCompat#postWebMessage(android.webkit.WebView,androidx.webkit.WebMessageCompat,android.net.Uri)) or
[`WebMessagePort.postMessage`](https://developer.android.com/reference/android/webkit/WebMessagePort#postMessage(android.webkit.WebMessage)) to communicate with the JavaScript
[`Window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). `WebViewCompat.postWebMessage` and
`WebMessagePort.postMessage` can accept JavaScript messages sent through
`Window.postMessage` which will be executed within the WebView.

There are multiple risks associated with native bridges:

- JavascriptInterface-based bridges:
  - The `addJavascriptInterface` method injects a supplied Java object into every frame of the WebView, including iframes, which means it is susceptible to attack by malicious third parties injecting frames into a legitimate website. Applications targeting API level 16 or earlier are particularly at risk of attack because this method can be used to allow JavaScript to control the host application.
  - Reflecting untrusted user-provided content in native bridge-enabled WebViews allows for cross-site scripting (XSS) attacks.
- MessageChannel-based bridges:
  - Lack of origin checks on message channel endpoints means that messages will be accepted from any sender, including those containing malicious code.
  - It is possible to accidentally expose Java to arbitrary JavaScript.

## Impact

The `addJavascriptInterface`, `postWebMessage`, and `postMessage` methods can be
leveraged by malicious actors to access, manipulate, or inject code they control
into a WebView. This may lead to users being redirected to malicious sites,
loading malicious content, or having malicious code run on their devices that
can extract sensitive data or achieve privilege escalation.

## Risk: addJavascriptInterface risks

WebView implements basic functionalities of a browser, such as page rendering,
navigation, and JavaScript execution. WebView can be used inside an application
to display web content as part of an activity layout. Implementing a native
bridge within a WebView using the `addJavascriptInterface` method can create
security issues such as cross-site scripting (XSS), or allow attackers to load
untrusted content through interface injection and manipulate the host
application in unintended ways, executing Java code with the permissions of the
host application.

### Mitigations

#### Disable JavaScript

In scenarios where WebView doesn't require JavaScript, don't call
[`setJavaScriptEnabled`](https://developer.android.com/reference/android/webkit/WebSettings#setJavaScriptEnabled(boolean)) within [`WebSettings`](https://developer.android.com/reference/android/webkit/WebSettings) (for example, while
displaying static HTML content). By default, JavaScript execution is disabled in
WebView.

#### Remove JavaScript interface when loading untrusted content

Ensure objects from the JavaScript interface are removed by calling
[`removeJavascriptInterface`](https://developer.android.com/reference/android/webkit/WebView#removeJavascriptInterface(java.lang.String)) before untrusted content is loaded by the
WebView. For example, this can be done in a call to
[`shouldInterceptRequest`](https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest)).  

### Kotlin

    webView.removeJavascriptInterface("myObject")

### Java

    webView.removeJavascriptInterface("myObject");

#### Only load web content over HTTPS

If you need to load untrusted content, ensure the WebView loads web content over
an encrypted connection (see also our guidelines on [Cleartext
Communications](https://developer.android.com/privacy-and-security/risks/cleartext-communications)). Prevent the initial page load from being performed on
unencrypted connections by setting `android:usesCleartextTraffic` to `false` in
the `AndroidManifest` file or disallow HTTP traffic in a [network security
config](https://developer.android.com/training/articles/security-config). See the [`usesCleartextTraffic`](https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic) documentation for more
information.  

### Xml

    <application
        android:usesCleartextTraffic="false">
        <!-- Other application elements -->
    </application>

To ensure that redirects and further app browsing doesn't occur on unencrypted
traffic, check for the HTTP scheme in [`loadUrl`](https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String)) or
`shouldInterceptRequest`:  

### Kotlin

    fun loadSecureUrl(webView: WebView?, url: String?) {
        webView?.let { wv ->  // Ensure valid WebView and URL
            url?.let {
                try {
                    val uri = URI(url)
                    if (uri.scheme.equals("https", ignoreCase = true)) { // Enforce HTTPS scheme for security
                        wv.loadUrl(url)
                    } else {
                        // Log an error or handle the case where the URL is not secure
                        System.err.println("Attempted to load a non-HTTPS URL: $url")
                    }
                } catch (e: Exception) {
                    // Handle exception for improper URL format
                    System.err.println("Invalid URL syntax: $url")
                }
            }
        }
    }

### Java

    public void loadSecureUrl(WebView webView, String url) {
        if (webView != null && url != null) { // Ensure valid WebView and URL
            try {
                URI uri = new URI(url);
                String scheme = uri.getScheme();
                if ("https".equalsIgnoreCase(scheme)) { // Enforce HTTPS scheme for security
                    webView.loadUrl(url);
                } else {
                    // Log an error or handle the case where the URL is not secure
                    System.err.println("Attempted to load a non-HTTPS URL: " + url);
                }
            } catch (URISyntaxException e) {
                // Handle exception for improper URL format
                System.err.println("Invalid URL syntax: " + url);
            }
        }
    }

#### Validate untrusted content

If any external links are loaded in a WebView, validate both scheme and host
(allowlist domains). Any domains not in the allowlist should be opened by the
default browser instead.

#### Don't load untrusted content

If possible, only load strictly scoped URLs and content owned by the app
developer into WebView.

#### Don't expose sensitive data

If your application accesses sensitive data with a WebView, consider using the
[`clearCache`](https://developer.android.com/reference/android/webkit/WebView#clearCache(boolean)) method to delete any files stored locally, before using the
JavaScript interface. You can also use server-side headers, such as no-store, to
indicate that an application shouldn't cache particular content.

#### Don't expose sensitive functionalities

If your application requires sensitive permissions or collects sensitive data,
ensure that it is called from code within the application and that prominent
disclosure is provided to users. Avoid using JavaScript interfaces for any
sensitive operations or user data.

#### Target API level 21 or higher

One secure way to use the `addJavascriptInterface` method is to target API level
21 or higher by ensuring the method is called only when running on API level 21
or higher. Before API 21, JavaScript could use reflection to access the public
fields of an injected object.

*** ** * ** ***

## Risk: MessageChannel risks

Lack of origin control in `postWebMessage()` and `postMessage()` could allow
attackers to intercept messages or send messages to native handlers.

### Mitigations

When setting up `postWebMessage()` or `postMessage()`, only allow messages from
trusted domains by avoiding the use of \* as the target origin, and instead
explicitly specify the expected sending domain.

*** ** * ** ***

## Resources

- [postMessage() best practices](https://fastercapital.com/content/Channel-messaging--Best-Practices-for-Implementing-Channel-Messaging-in-JavaScript.html)
- [addJavascriptInterface documentation](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String))
- [postMessage() documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#the_dispatched_event)
- [WebMessagePort.postMessage() documentation](https://developer.android.com/reference/android/webkit/WebMessagePort#postMessage(android.webkit.WebMessage))
- [WebViewClient.shouldInterceptRequest documentation](https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest))
- [Documentation of security advice regarding addJavascriptInterface](https://developer.android.com/docs/quality-guidelines/core-app-quality#sc)
- [clearCache documentation](https://developer.android.com/reference/android/webkit/WebView#clearCache(boolean))
- [removeJavascript documentation](https://developer.android.com/reference/android/webkit/WebView#removeJavascriptInterface(java.lang.String))
- [enabling JavaScript in WebViews](https://developer.android.com/develop/ui/views/layout/webapps/webview#EnablingJavaScript)