A WebView's default navigation policy is wrong in both directions. Leave it alone and every link — including the bank's payment page and the phishing clone — opens inside your own WebView, leaking your referrer, your session cookie, and, in real disclosed CVEs, your auth token right in the URL bar. Touch it and you ship ERR_UNKNOWN_URL_SCHEME crashes the first time a user taps a phone number. And because there is no host allowlist by default, a single unguarded bridge call becomes silent account takeover. The host-owned runtime decides, per link and per origin, what leaves and what stays.
The default is «open everything in here»
By default a WebView loads every https destination internally. A tap on a partner's shop page, a banking redirect, or even an external news article renders inside your app's own chrome. That is convenient until it is a liability: the destination site now receives your Referer header, any token you appended to the URL, and the persistent identifier your WebView shipped in its User-Agent. In 2026 the Mira Android app was disclosed (CVE-2026-66832, CVSS 6.5) for exactly this — it appended the user's live session token to the destination URL as a query string and put a stable user ID in the WebView's User-Agent, transmitting both to third-party properties, referrer logs, and any script executing in the WebView. That is account takeover, not a hypothetical.
When you fix it badly, you ship crashes
The common reaction is to override shouldOverrideUrlLoading and route non-http schemes out to the system. Get the boolean backwards and nothing happens; miss tel:, mailto:, sms:, geo:, or intent:// and the user gets ERR_UNKNOWN_URL_SCHEME — the WebView simply cannot render a scheme it does not understand. On Android 7+ (API 24) the new method signature is mandatory, and a wrong return value throws ActivityNotFoundException and crashes. Android 11+ adds package-visibility rules, so a deep link to another app silently fails unless declared. The fix is real but fiddly, and it lives in native code the web team does not own.
No allowlist means the bridge is an open door
The deeper failure is structural. Navigation policy and the JS bridge are the two guards between web content and native power, and in a published audit (SilentGrid) both were absent: the bridge returned the live auth token to any JavaScript on the page, and the WKWebView's decidePolicyFor allowed any https origin with no host check. One crafted deep link was enough to load an attacker page, call the token handler, and exfiltrate the session — in a single tap, with the victim seeing nothing. Reverse tabnabbing compounds it: a page opened without rel="noopener" can silently redirect your opener to a phishing clone, and WebViews do not apply the browser's protective default.
The runtime fix
A host-owned runtime makes the navigation boundary explicit and enforced. It maintains a host allowlist: first-party domains load in the runtime, known app schemes (tel:, mailto:, market:) are routed to the system with safe fallbacks, and everything else is either opened in an external browser or blocked. The JS bridge is origin-checked and least-privilege, so a page from an untrusted catalog entry cannot reach a credential handler. The link that should leave, leaves; the token that should stay, stays.
| Dimension | WebView | Host-owned runtime |
|---|---|---|
| Default behavior | Every https opens inside the WebView | First-party loads inside; rest leaves or is blocked |
| External link | Referer, URL token, UA ID leaked to third party | Host allowlist governs every origin |
| Custom schemes | ERR_UNKNOWN_URL_SCHEME crash if unhandled | Routed to system with safe fallback |
| JS bridge | Often no origin check | Origin-checked, least-privilege |
| Tabnabbing | noopener not enforced | Forced on every new window |
The through-line of this series is control. A WebView hands your navigation boundary a default that is unsafe in both directions and a bridge that trusts whoever calls it. A runtime makes the boundary a policy you write — per link, per origin, per catalog entry. For a catalog of other people's pages transacting inside your app, that is the difference between a controlled storefront and an open door.
Sources
- CVE-2026-66832 — Mira Android app leaks live session token in URL query and persistent ID in WebView User-Agent to third parties (CVSS 6.5) — disclosed 2026: auth material and tracking identifier transmitted beyond the app boundary during in-app WebView flows.
- Stealing Auth Tokens Through the WebView Bridge — a JS bridge with no origin check plus a navigation policy with no host allowlist chains into silent account takeover — SilentGrid audit: one crafted deep link, one tap, token exfiltrated.
- Android WebView and App Links: the default behavior loads everything internally — App Links, tel:, mailto:, intent:// all trapped inside the WebView unless intercepted.
- ERR_UNKNOWN_URL_SCHEME: WebView renders only http/https/file/data/about; every other scheme must be an Intent — the canonical fix and why the crash appears only at login, payment, or share steps.
- Reverse tabnabbing in mobile WebViews — opener reference is abusable without noopener, and WebViews may not apply the browser default — how an opened page silently redirects your opener to phishing.