Scope & ethics: Everything below is for authorized, lab-only testing and for hardening your own apps. The module covers Android WebView, in-app browsers, and hybrid stacks (Cordova/CAPacitor, React Native, Flutter, Ionic), with a focus on JavaScript bridges, navigation, storage, cookies, OAuth flows, and server-side headers. We emphasize defensive patterns, repeatable lab procedures, and audit-grade evidence.
17.0 Learning Objectives
After this module, you will be able to:
- Enumerate and map all web surfaces in a mobile app: WebViews, in-app browsers, JS bridges, deep links, custom schemes.
- Test for dangerous WebView settings and code execution via
addJavascriptInterface,loadUrl("javascript:…"), file-URL exposure, and universal XSS. - Assess hybrid frameworks (Cordova, React Native, Flutter WebView, Capacitor, Ionic) for plugin/bridge misuse, insecure configs, and supply-chain risks.
- Validate OAuth/OpenID flows in in-app browsers vs system browser/custom tabs; identify anti-phishing and token leakage issues.
- Harden cookie/session handling (SameSite, HttpOnly, Secure), CSP, HSTS, mixed content, and local web storage at rest.
- Create lab repros, collect evidence, and deliver developer-ready mitigations and CI checks.
17.1 Threat Model (Web/Hybrid on Android)
- Untrusted content inside privileged WebView: remote or local HTML/JS can escalate to app privileges via JS bridge.
- Navigation & origin confusion: open redirects, deep links, or
postMessagemisuse can move users to hostile origins. - Cookie & token theft: weak cookie flags (no
HttpOnly/Secure/SameSite), tokens in URL fragments, WebView reading cookies. - File/asset exposure:
file://orcontent://paths exposed to the web origin, path traversal inshouldInterceptRequest. - Supply chain: vulnerable Cordova/Capacitor plugins, unsafe RN native modules, outdated WebView/Chromium engine on old devices.
- Phishing & OAuth: using WebView for login; lack of URL identity/UI affordances; custom scheme capture by other apps.
17.2 Recon & Asset Mapping
Create a Web Surface Inventory:
| Surface | Type | Entry | Origin(s) | JS Bridge? | Auth? | Notes |
|---|---|---|---|---|---|---|
| MainWebView | Android WebView | Activity WebActivity | https://app.example.com | addJavascriptInterface(AppBridge,"App") | Yes | Loads remote + local assets |
| OAuth | Custom Tab | CustomTabsIntent | https://idp.example.com | No | Yes | PKCE, system browser |
| Reports | WebView | ReportsActivity | file:///android_asset/ | No | No | Local HTML, templates |
How to collect:
- Static: grep project for
WebView,WebSettings,addJavascriptInterface,loadUrl,loadDataWithBaseURL,WebViewClient,WebChromeClient,shouldOverrideUrlLoading,shouldInterceptRequest. - Manifest: intent filters (http/https/schemes), exported Activities that hold WebViews.
- Runtime: enable WebView devtools (developer options) to inspect pages.
17.3 Dangerous WebView Settings & What They Mean
17.3.1 Settings Checklist
| Setting | Safe Default | Risk When Enabled | Notes/Mitigation |
|---|---|---|---|
setJavaScriptEnabled | false | XSS → JS bridge abuse | Enable only when needed; pair with CSP, bridge scoping |
setAllowFileAccess | false | file:// read of app assets | Keep disabled unless local assets required |
setAllowFileAccessFromFileURLs | false | Local → local escalation | Keep false |
setAllowUniversalAccessFromFileURLs | false | Local → network cross-origin | Keep false |
setDomStorageEnabled | false | Persistent local storage exposure | Enable only if necessary; encrypt at rest if mirrored |
setMixedContentMode | MIXED_CONTENT_NEVER_ALLOW | MitM downgrade | Avoid loading HTTP in HTTPS pages |
setSavePassword | N/A | Credential leakage | Not supported in modern WebView; don’t implement custom saves |
addJavascriptInterface | N/A | Native code invocation | Avoid for remote origins; annotate methods with @JavascriptInterface, use per-origin gating |
Code smell:
web.settings.javaScriptEnabled = true
web.settings.allowFileAccessFromFileURLs = true // ❌
web.settings.allowUniversalAccessFromFileURLs = true // ❌
web.addJavascriptInterface(AppBridge(context), "Android") // ⚠︎
17.4 JavaScript Bridges (addJavascriptInterface) — Risks & Hardening
17.4.1 Attack Surface
- Methods annotated with
@JavascriptInterfaceare callable by JS in the WebView. - If remote content (network) can execute JS → can call bridge → native privilege escalation (file read, intents, keystore access if exposed).
- If XSS exists on trusted origin → same outcome.
17.4.2 Audit Steps
- Locate all bridges & annotated methods:
grep -RIn "@JavascriptInterface" app/src - Map which WebViews inject which bridges, and which URLs/origins they load.
- Review method bodies for sensitive operations: file I/O, intents, clipboard, logs, tokens.
17.4.3 Lab Tests (authorized)
- XSS/Bridge call (only on lab origins): inject
javascript:Android.readFile('/data/data/...')via a controlled page or vialoadUrl("javascript:...")to confirm bridge exposure. - Origin gating check: implement
shouldOverrideUrlLoadingand verify it blocks untrusted origins before they can call bridges.
17.4.4 Hardening Patterns
- Never expose bridges to remote content. Bridges are for local assets or trusted, verified origins only.
- Implement an origin allow-list inside the Activity/
WebViewClient:override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { val url = request.url val allowed = setOf("app.example.com") return if (url.host in allowed && (url.scheme == "https")) false else true } - Use message channels (
postMessageor JS <-> native channels) that carry a nonce/session token validated server-side. - Remove global bridges; instead, add per-page, short-lived bridges and remove with
removeJavascriptInterface.
17.5 Navigation, URL Handling & Deep Links
17.5.1 shouldOverrideUrlLoading & shouldInterceptRequest
- Pitfall: returning
falseblindly → any origin can load. - Mitigation: whitelist hosts; block
file://,content://unless deliberate; scrutinizeintent://URLs.
17.5.2 Open Redirects & Path Confusion
- If server app has open redirects, attacker can move WebView to hostile origin; now JS can attack.
- Mitigation: server-side fix; on client, enforce origin checks at every navigation.
17.5.3 Custom Schemes & Intent URIs
intent://ormybank://handlers can be abused to exfiltrate data via URL parameters if the app reflects them.- Validate scheme handlers and sanitize parameters.
17.6 OAuth/OIDC in Mobile — WebView vs Custom Tabs vs System Browser
17.6.1 Risks Using WebView for Login
- No shared cookie jar with system browser; can’t leverage SSO controls.
- Easy to phish: no trusted URL bar; app can forge UI.
- Token delivery via URL fragments → risk of leakage to page scripts or logs.
17.6.2 Recommended Pattern
- Use Android Custom Tabs (or system browser) + PKCE for OAuth.
- Never deliver tokens to page JS; use AppAuth-style flows; receive code in app via app link with verified domain.
- Enforce verified app links to prevent other apps from intercepting auth callback.
17.6.3 Lab Validation
- Confirm PKCE: check that a
code_challenge(S256) is sent and the server validatescode_verifier. - Confirm callback handled by verified app link (Android App Links) and not by WebView JS.
17.7 Cookies, Storage & Headers
17.7.1 Cookie Flags
- Secure: only over HTTPS.
- HttpOnly: JS cannot read (prevents XSS stealing).
- SameSite:
LaxorStrictto reduce CSRF and cross-site leakage. - For WebView using
CookieManager, ensure cookies are configured safely; avoid exposing session cookies to JS.
17.7.2 Security Headers (server)
- CSP (Content-Security-Policy): strict source lists; disallow
unsafe-inlineif possible; use nonces. - HSTS:
Strict-Transport-Securityfor origins (outside WebView also benefits). - X-Frame-Options
/frame-ancestors`: prevent clickjacking in embedded flows. - Referrer-Policy: limit sensitive URL leakage.
17.7.3 Mixed Content
- Block HTTP resources on HTTPS pages:
web.settings.mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
17.8 Local Files, Assets & shouldInterceptRequest
- Risk: if you proxy requests via
shouldInterceptRequest, ensure you do not serve local files to untrusted origins; checkrequest.url. - Block
file://andcontent://unless using explicit safe paths. - Validate paths; avoid
../traversal into internal storage. - If you render local HTML (
file:///android_asset/…), do not enable universal access from file URLs; never allow that page to reach out to the internet unchecked.
17.9 Hybrid Frameworks — Special Considerations
17.9.1 Cordova/Ionic/Capacitor
config.xml/capacitor.config.*may allow wildcard navigations (<allow-navigation href="*">) — avoid.- Plugins expose native capabilities to JS; audit plugin list and versions.
- Disable
allow-intentwildcards; pin to specific schemes/hosts. - Enforce CSP aggressively; disable eval if possible (
cordova-plugin-whitelistconfigs).
17.9.2 React Native
- RN uses a JSCore/V8 environment; native modules bridge to Java/Kotlin.
- Audit native modules for sensitive APIs; ensure origin checks if modules can be triggered via web content (e.g., RN WebView).
- For
react-native-webview, avoidjavaScriptEnabledunless required; restrictoriginWhitelist; useonShouldStartLoadWithRequestto vet navigations.
17.9.3 Flutter WebView
- Check
javascriptMode, navigation delegates, and asset loaders. - Validate routes and deep links; avoid permissive asset handlers.
17.10 Practical Lab Playbooks (Authorized)
17.10.1 WebView Settings Audit (device)
- Navigate app to screens with WebViews.
- Dump app logs and class names (use jadx/grep) to find the hosting Activity.
- Use a debug build to print WebSettings at runtime (or instrument with Frida in a lab build).
- Evidence: a table of actual settings vs recommended.
Frida snippet (lab-only):
Java.perform(function() {
var WebView = Java.use('android.webkit.WebView');
WebView.getSettings.implementation = function() {
var s = this.getSettings();
console.log("[WebView] " + this + " JS=" + s.getJavaScriptEnabled() +
" FileAccess=" + s.getAllowFileAccess() +
" UAFromFile=" + s.getAllowFileAccessFromFileURLs() +
" UAUniversal=" + s.getAllowUniversalAccessFromFileURLs() +
" Mixed=" + s.getMixedContentMode());
return s;
};
});
17.10.2 JS Bridge Reachability
- Inject
javascript:URLs (lab page only) to attempt calling exposed interfaces; prove whether origin controls are enforced. - Evidence: screenshots/logs, call traces, and explicit hostnames of content used.
17.10.3 OAuth Flow Verification
- Capture an end-to-end login with a test IdP:
- Confirm Custom Tabs, PKCE, and verified app links.
- Check that no tokens are written to WebView DOM storage or accessible cookies (HttpOnly enforced).
17.10.4 Mixed Content & Header Tests
- Use a controlled page that attempts to load HTTP image/script on HTTPS; ensure it fails.
- Check response headers for CSP/HSTS/Referrer-Policy in Burp/mitmproxy (lab).
17.11 Hardening Cookbook (Drop-in Code & Policies)
17.11.1 Safe WebView Initialization (Kotlin)
fun secureInit(web: WebView, allowedHosts: Set<String>) {
web.settings.apply {
javaScriptEnabled = false
domStorageEnabled = false
allowFileAccess = false
allowContentAccess = false
mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
}
// Remove default dangerous interfaces (older devices)
web.removeJavascriptInterface("searchBoxJavaBridge_")
web.removeJavascriptInterface("accessibility")
web.removeJavascriptInterface("accessibilityTraversal")
web.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(v: WebView, r: WebResourceRequest): Boolean {
val u = r.url
val ok = u.scheme == "https" && allowedHosts.contains(u.host?.lowercase())
return !ok
}
}
}
Enable JS only per page when required and only after verifying origin.
17.11.2 Origin-Gated Bridge
class AppBridge(private val origin: String) {
@JavascriptInterface
fun echo(input: String): String {
// Minimal example – real bridges should check a bound, verified origin token.
return input.take(256)
}
}
// When page navigates and origin verified:
if (origin == "app.example.com") {
web.addJavascriptInterface(AppBridge(origin), "App")
}
17.11.3 Custom Tabs for OAuth (PKCE)
- Use AppAuth for Android or equivalent; do not roll your own WebView login.
- Ensure Verified App Links for the callback URL and register only the official package.
17.11.4 Server Headers (Minimal Set)
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-<dynamic>'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
17.12 Evidence & Severity Rubric
Collect: code snippets (decompiled or source), runtime logs, Frida traces (lab), HTTP captures, screenshots, and a matrix of settings/headers. Hash artifacts; redact tokens.
Severity (examples):
- Critical: Remote content in WebView with
addJavascriptInterfacereachable from untrusted origin → native code execution path; OAuth performed in raw WebView with tokens accessible to JS. - High:
allowUniversalAccessFromFileURLsorallowFileAccessFromFileURLsenabled with local content; mixed content permitted on sensitive origins. - Medium: Missing CSP/HSTS on sensitive pages; permissive navigation or origin whitelist; weak SameSite on session cookies.
- Low: Minor header gaps; non-sensitive bridges with strict gating; cosmetic issues.
17.13 CI & Automation
- Static checks (Semgrep/Android Lint):
- Flag
setAllowUniversalAccessFromFileURLs(true),setAllowFileAccessFromFileURLs(true),addJavascriptInterfaceusage. - Flag
mixedContentMode != NEVER_ALLOW. - Flag any OAuth implemented with
WebViewrather than Custom Tabs.
- Flag
- MobSF: Detects risky WebView settings, file access, JS interface patterns.
- Unit/Instrumented tests:
- Load test HTML; assert
shouldOverrideUrlLoadingblocks foreign origins. - Assert no bridges injected for remote origins; verify JS-needed pages only.
- Load test HTML; assert
- Release gates:
- Fail builds when forbidden settings are found without explicit justification (waiver process with security sign-off).
17.14 Special Topics
17.14.1 postMessage & Window Messaging
- Always check
event.originon message receipt (web side). - Do not trust
*as target origin; specify exact origin.
17.14.2 Download Handlers & File Choosers
- Validate MIME types and destination folders; avoid writing to public external storage; prefer SAF (Storage Access Framework).
17.14.3 Printing & PDF Rendering
- Be careful with temporary files; ensure they are in app-private storage and cleared after operation.
17.14.4 Internationalization & RTL Phishing
- Beware of URL spoofing with RTL marks or homoglyphs; UI should display canonicalized/verified host (for sensitive flows, prefer system browser UI).
17.15 Labs (Reproducible, Authorized)
Lab 17-A — Bridge Exposure
- Create a local page with
alert(Android.echo("ping")). - Ensure in production settings this fails (no bridge or origin blocked).
- Document before/after hardening.
Lab 17-B — OAuth Correctness
- Implement PKCE flow via Custom Tabs to a test IdP; confirm no tokens reach WebView/JS storage; validate verified app links.
Lab 17-C — Mixed Content & CSP
- Host a lab page with CSP and attempt inline scripts; confirm nonce required; attempt HTTP resource on HTTPS page and verify block.
Lab 17-D — Hybrid Config Audit
- Cordova/Ionic: lock down
<allow-navigation>and<allow-intent>; prove previous wildcards led to unsafe navigation.
17.16 Deliverables
- Web Surface Inventory (table of WebViews, origins, bridges, settings).
- Risk & Header Matrix (CSP/HSTS/SameSite, cookie audit).
- Lab Evidence Pack (commands, logs, screenshots, hashed).
- Hardening PRs (code diffs: WebView init, origin checks, bridge gating, Custom Tabs OAuth).
- CI Ruleset (Semgrep/Lint configs) and waiver process.
17.17 “At a Glance” Cheat Sheet
- Don’t log in via WebView. Use Custom Tabs + PKCE + verified app links.
- Default-deny WebView. JS off, file access off, mixed content off.
- Bridges are sharp knives. Avoid on remote content; origin-gate; remove when not needed.
- Whitelist navigation. Block unknown hosts,
file://,content://,intent://by default. - Harden server. CSP, HSTS, Secure/HttpOnly/SameSite cookies.
- Automate checks. CI rules to block dangerous settings and wildcard hybrid configs.

Hello There. I found your blog using msn. This is a really well written article.
I will make sure to bookmark it and return to read more of your useful
information. Thanks for the post. I’ll definitely return.
You’re welcome, I’m glad you liked it.
Everything is very open with a really clear description of the issues.
It was truly informative. Your website is very helpful.
Thanks for sharing!
You’re welcome, I’m glad you liked it.