Module 17 — WebView, Hybrid & In-App Browser Security

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 postMessage misuse 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:// or content:// paths exposed to the web origin, path traversal in shouldInterceptRequest.
  • 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:

SurfaceTypeEntryOrigin(s)JS Bridge?Auth?Notes
MainWebViewAndroid WebViewActivity WebActivityhttps://app.example.comaddJavascriptInterface(AppBridge,"App")YesLoads remote + local assets
OAuthCustom TabCustomTabsIntenthttps://idp.example.comNoYesPKCE, system browser
ReportsWebViewReportsActivityfile:///android_asset/NoNoLocal 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

SettingSafe DefaultRisk When EnabledNotes/Mitigation
setJavaScriptEnabledfalseXSS → JS bridge abuseEnable only when needed; pair with CSP, bridge scoping
setAllowFileAccessfalsefile:// read of app assetsKeep disabled unless local assets required
setAllowFileAccessFromFileURLsfalseLocal → local escalationKeep false
setAllowUniversalAccessFromFileURLsfalseLocal → network cross-originKeep false
setDomStorageEnabledfalsePersistent local storage exposureEnable only if necessary; encrypt at rest if mirrored
setMixedContentModeMIXED_CONTENT_NEVER_ALLOWMitM downgradeAvoid loading HTTP in HTTPS pages
setSavePasswordN/ACredential leakageNot supported in modern WebView; don’t implement custom saves
addJavascriptInterfaceN/ANative code invocationAvoid 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 @JavascriptInterface are 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

  1. Locate all bridges & annotated methods: grep -RIn "@JavascriptInterface" app/src
  2. Map which WebViews inject which bridges, and which URLs/origins they load.
  3. 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 via loadUrl("javascript:...") to confirm bridge exposure.
  • Origin gating check: implement shouldOverrideUrlLoading and 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 (postMessage or 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 false blindly → any origin can load.
  • Mitigation: whitelist hosts; block file://, content:// unless deliberate; scrutinize intent:// 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:// or mybank:// 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 validates code_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: Lax or Strict to 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-inline if possible; use nonces.
  • HSTS: Strict-Transport-Security for 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; check request.url.
  • Block file:// and content:// 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-intent wildcards; pin to specific schemes/hosts.
  • Enforce CSP aggressively; disable eval if possible (cordova-plugin-whitelist configs).

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, avoid javaScriptEnabled unless required; restrict originWhitelist; use onShouldStartLoadWithRequest to 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)

  1. Navigate app to screens with WebViews.
  2. Dump app logs and class names (use jadx/grep) to find the hosting Activity.
  3. Use a debug build to print WebSettings at runtime (or instrument with Frida in a lab build).
  4. 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 addJavascriptInterface reachable from untrusted origin → native code execution path; OAuth performed in raw WebView with tokens accessible to JS.
  • High: allowUniversalAccessFromFileURLs or allowFileAccessFromFileURLs enabled 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), addJavascriptInterface usage.
    • Flag mixedContentMode != NEVER_ALLOW.
    • Flag any OAuth implemented with WebView rather than Custom Tabs.
  • MobSF: Detects risky WebView settings, file access, JS interface patterns.
  • Unit/Instrumented tests:
    • Load test HTML; assert shouldOverrideUrlLoading blocks foreign origins.
    • Assert no bridges injected for remote origins; verify JS-needed pages only.
  • 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.origin on 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.

4 comentários em “Module 17 — WebView, Hybrid & In-App Browser Security”

  1. 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.

Os comentários estão encerrado.