November 2, 2026
The Bridge Between a Web Page and the Phone Itself: Abusing an Android WebView JavaScript Interface
Hybrid mobile apps occupy an unusual security position: part native application, part embedded web browser, with a deliberate bridgeβ¦

By T4nv1
4 min read
Hybrid mobile apps occupy an unusual security position: part native application, part embedded web browser, with a deliberate bridge connecting the two so that web content running inside the app can trigger native device functionality β things no ordinary website could ever do, like reading contacts, accessing the camera, or writing files to the device's storage. That bridge is enormously useful for building rich, native-feeling features quickly. It's also one of the most consequential places a mobile app's security model can quietly collapse, because the web content given access to that bridge isn't always as trustworthy as the app's own developers assumed.
The target, which I'll call Marrow Street here per their program's terms, is a retail app embedding a WebView to display product listings and support chat content, loaded from a mix of the company's own domain and, during support chat specifically, a third-party vendor's hosted chat widget.
What a JavaScript Interface Actually Grants
Android's WebView allows a native app to expose specific Java objects directly to JavaScript running inside the web content it displays, through a mechanism that effectively hands the web page a callable native API. Decompiling Marrow Street's APK and reading the relevant native code revealed exactly such a bridge, exposing an object named NativeBridge to any JavaScript running inside the app's WebView:
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
public class NativeBridge {
@JavascriptInterface
public void downloadFile(String url, String filename) {
// downloads the given URL to device storage
}
@JavascriptInterface
public String getAuthToken() {
return SharedPrefs.getAuthToken();
}
@JavascriptInterface
public void openNativeScreen(String screenName, String params) {
// navigates the native app to a given screen with parameters
}
}webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
public class NativeBridge {
@JavascriptInterface
public void downloadFile(String url, String filename) {
// downloads the given URL to device storage
}
@JavascriptInterface
public String getAuthToken() {
return SharedPrefs.getAuthToken();
}
@JavascriptInterface
public void openNativeScreen(String screenName, String params) {
// navigates the native app to a given screen with parameters
}
}Three methods, each annotated to be callable directly from JavaScript: one downloading an arbitrary URL to device storage, one returning the user's actual authentication token as a plain string, and one triggering native navigation based on a string parameter. Any of these, reachable from untrusted web content, represents a serious problem β together, they're close to a complete native-app compromise achievable entirely from JavaScript.
Why "Our Own Domain Only" Isn't the Whole Story
The natural first question is whether this bridge was actually reachable from anything other than Marrow Street's own, presumably trustworthy web content. Reading further into the WebView's configuration revealed no domain restriction on which pages were permitted to load within it at all β any URL the WebView was navigated to, by any means, would have full access to the exposed NativeBridge object, not just pages served from Marrow Street's own domain.
More specifically and more immediately exploitable: the support chat feature embedded a third-party vendor's chat widget directly inside the same WebView instance that had the bridge attached, meaning that widget's JavaScript β code written and controlled entirely by a separate company, updated on their own schedule, outside Marrow Street's own review process β had full, standing access to call getAuthToken(), downloadFile(), and openNativeScreen() at any time, with no indication to the user that anything beyond a chat conversation was happening.
Confirming the Reach, Safely
I built a minimal test page replicating the chat widget's loading context as closely as I could determine from the app's own configuration, and had it call the bridge's methods directly:
if (window.NativeBridge) {
const token = window.NativeBridge.getAuthToken();
console.log("Retrieved token:", token);
}if (window.NativeBridge) {
const token = window.NativeBridge.getAuthToken();
console.log("Retrieved token:", token);
}Loaded within the app's WebView context on my own test device, logged under my own test account, this confirmed the bridge was reachable and that getAuthToken() returned a genuine, functional authentication token for my own session β a token that, used directly against Marrow Street's API, would grant full account access without any further authentication step, entirely extracted through JavaScript with no native-level interaction required at all.
Why a Third-Party Script Having This Access Is the Real Severity Driver
It's worth being direct about why this rates considerably higher than "an app has a JavaScript bridge," which on its own, scoped correctly and restricted to fully trusted content, is a legitimate and common pattern. The severity here comes specifically from the combination: a bridge exposing account-compromising functionality, with no restriction on which web content can reach it, embedding a third party's actively-served, externally-controlled JavaScript directly into that same unrestricted context. Any compromise of that third-party vendor's own infrastructure, or any vulnerability in their own widget code allowing injected script execution, would translate directly and immediately into full account takeover for every Marrow Street user with the chat feature open, entirely independent of any security review Marrow Street's own team had ever performed on their own code.
Scope
All testing was performed against my own account's token on a device I controlled, and I did not attempt to actually exploit the third-party chat widget itself to prove the end-to-end attack chain, since doing so would require testing against infrastructure outside Marrow Street's own program scope β confirming the bridge's unrestricted reach and the sensitivity of what it exposed was sufficient to demonstrate the complete risk.
The Report
Title: Unrestricted JavaScript bridge exposes authentication token and native functionality to any web content loaded in the WebView, including an embedded third-party chat widget
Root cause: The app exposes a JavaScript interface granting access to the user's authentication token, arbitrary file downloads, and native navigation, with no restriction on which origins are permitted to load within the WebView instance this bridge is attached to. A third-party vendor's chat widget, loaded within the same WebView, consequently has standing access to this entire bridge.
Reproduction: Decompiled bridge implementation showing the exposed methods, confirmation that the WebView applies no origin restriction, and a successful token extraction performed against my own account from a test page replicating the chat widget's loading context.
Impact: Any compromise or malicious update of the third-party chat widget's code, or any future first-party content loaded in this WebView from an insufficiently trusted source, can extract any user's authentication token and achieve full account takeover, independent of Marrow Street's own application security.
Fix recommendations:
- Restrict the WebView to only load content from an explicit allowlist of fully trusted, first-party origins, rejecting navigation to any other domain outright
- Remove
getAuthToken()from the exposed bridge entirely; if native code genuinely needs to pass an authenticated token to web content, use a short-lived, narrowly scoped token generated specifically for that purpose rather than exposing the user's actual primary session token - Load the third-party chat widget in a separate, isolated WebView instance with no bridge attached at all, or evaluate whether the vendor offers an SDK-based integration that avoids this shared-context risk entirely
What Happened After
Marrow Street removed the auth-token method from the bridge immediately, restricted WebView navigation to an explicit first-party allowlist, and isolated the third-party chat widget into its own, bridge-free WebView instance as a structural fix addressing the underlying shared-context risk rather than only the immediate token exposure.
Why Hybrid App Bridges Deserve Dedicated Review
Any hybrid mobile app combining a WebView with a native JavaScript bridge deserves a specific, deliberate review of two separate questions: what does the bridge actually expose, and which web content is actually permitted to reach it. The two questions compound β a powerful bridge restricted to fully trusted content is reasonable; a powerful bridge reachable from any loaded content, especially content controlled by a third party, is a standing compromise waiting for its first opportunity.