WebView JavaScript interfaces expose native application methods to JavaScript code running in an embedded web view. If the web view loads untrusted content, any script — including attacker-controlled code — can call those native methods directly.

Why is this an issue?

Once a JavaScript interface is registered on a web view, the exposed native object is accessible to all JavaScript running in that web view, including scripts in untrusted third-party iframes. Web views may load content from sources that are not fully trusted, and JavaScript interfaces are propagated to every frame, not just the top-level page. An attacker who can inject or control JavaScript in any frame can therefore call the exposed native methods directly.

What is the potential impact?

Unauthorized access to sensitive data or functionality

An attacker who controls JavaScript in the WebView — for example, through a malicious third-party iframe embedded in an otherwise legitimate page — can invoke exposed native methods without restriction. Depending on what the methods expose, this can lead to unauthorized access to sensitive user data, execution of privileged application functionality, or full compromise of the application’s security model.

How to fix it

Code examples

The following code is vulnerable because it registers a native interface that is accessible to all JavaScript running in the WebView, including JavaScript from untrusted sources such as third-party iframes.

Noncompliant code example

public class ExampleActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        WebView webView = new WebView(this);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new JavaScriptBridge(), "androidBridge"); // Noncompliant
    }

    public static class JavaScriptBridge {
        @JavascriptInterface
        public String accessUserData(String userId) {
            return getUserData(userId);
        }
    }
}

Compliant solution

public class ExampleActivity extends AppCompatActivity {
    private static final Set<String> ALLOWED_ORIGINS = Collections.singleton("https://example.com");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        WebView webView = new WebView(this);
        webView.getSettings().setJavaScriptEnabled(true);

        WebViewCompat.addWebMessageListener(
            webView,
            "androidBridge",
            ALLOWED_ORIGINS, // Only allow messages from these origins
            new WebMessageListener() {
                @Override
                public void onPostMessage(
                    WebView view,
                    WebMessageCompat message,
                    Uri sourceOrigin,
                    boolean isMainFrame,
                    JavaScriptReplyProxy replyProxy
                ) {
                    // Handle the message
                }
            }
        );
    }
}
Note If the interface cannot be replaced immediately, it can be removed before loading untrusted content.
webView.addJavascriptInterface(new JavaScriptBridge(), "androidBridge");
// ...
// Remove the interface before loading untrusted content
webView.removeJavascriptInterface("androidBridge");
webView.loadUrl("https://untrusted.example.com");

Resources

Documentation

Standards

Related rules