Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Overcoming Unsecurities in WebViews // Android ...

Overcoming Unsecurities in WebViews // Android Worldwide July 2024

My presentation about the insecurity you may feel due to using unsecure WebViews in your Android apps as presented on the July 30th 2024 Android Worldwide event.

See an upgraded version of this presentation: https://speakerdeck.com/balazsgerlei/droidcon-london-2024

Intro
Is your relationship with WebViews healthy? Sometimes you just can't avoid the need to display web content in your app. It can be a functionality that you need to push out quickly and it's already implemented by web developers in your team, or just a Terms and conditions page that you have to display in your app while it also needs to be updateable without going through a release cycle.

Either way, web content tends to make its way into many apps and it's not trivial that with just adding a single WebView, how much you can open up your app for abuse by malicious actors. Google made steady progress in making WebViews more secure but in most cases, you can't stop supporting those old, vulnerable Android versions and then it's your responsibility to secure your WebViews. This talk aims to help with that while also highlighting security issues that lurk in those seemingly simple yet quite complex APIs.

The talk will describe major security mistakes unaware developers can make when using WebViews in their app, how to fix them and what actual exploitation these can lead to if they remain in the app.

Key takeaways are to always sanitize inputs, restrict capabilities to what is actually needed (as WebView does not use secure defaults in a lot of cases), use more modern APIs like Custom Tabs, JavaScript Engine or AndroidX's WebView variant.

Links
My SecureWebView library:
https://github.com/balazsgerlei/SecureWebView

Chrome Remote Debugging
https://developer.chrome.com/docs/devtools/remote-debugging
https://developer.chrome.com/docs/devtools/console/javascript

Android Scoped Storage Demystified by Niharika Arora
https://betterprogramming.pub/android-scoped-storage-demystified-3024a062ba24

Evercookie by Samy Kamkar
https://github.com/samyk/evercookie

AndroidX Webkit library
https://developer.android.com/reference/androidx/webkit/package-summary.html
https://developer.android.com/jetpack/androidx/releases/webkit

Apache Commons Text library
https://github.com/apache/commons-text

OWASP Enterprise Security API (ESAPI)
https://owasp.org/www-project-enterprise-security-api/

AndroidX JavaScriptEngine library
https://developer.android.com/develop/ui/views/layout/webapps/jsengine
https://developer.android.com/jetpack/androidx/releases/javascriptengine

AndroidX Browser library (Custom Tabs)
https://developer.android.com/jetpack/androidx/releases/browser

Modern WebView best practices (Android Dev Summit ‘18)
https://youtu.be/HGZYtDZhOEQ

Android Security Evolution
https://balazsgerlei.com/AndroidSecurityEvolution

Executing JavaScript and WebAssembly
https://developer.android.com/develop/ui/views/layout/webapps/jsengine

Overview of Android Custom Tabs
https://developer.chrome.com/docs/android/custom-tabs/

HackTricks - Webview Attacks
https://book.hacktricks.xyz/mobile-pentesting/android-app-pentesting/webview-attacks

Balázs Gerlei

July 30, 2024
Tweet

More Decks by Balázs Gerlei

Other Decks in Programming

Transcript

  1. WebView History • Default way to display web content in

    Android apps • Built on top of the open-source Chromium project • Originally part of Android itself • Since Android 5.0 (API 21) it’s a separate app, distributed through Google Play Store • On Android 7 (API 24 & 25), 8 (API 26 & 27), and 9 (API 28), it is built into Chrome • From Android 10 (API 29), it is separate app again
  2. Relevant classes <<Abstract>> WebViewClient + shouldOverrideUrlLoading: Boolean + onLoadResource: void

    … WebView + loadUrl(url: String): void + loadData(data: String, mimeType: String, encoding: String): void … WebChromeClient + onProgressChanged(view: WebView, newProgress: Integer): void + onReceivedTitle(view: WebView, title: String): void … WebSettings + setJavaScriptEnabled (flag: Boolean): void + setTextZoom (textZoom: Integer): void …
  3. How to create a WebView <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" />

    val webView = WebView(activityContext) webView.webViewClient = WebViewClient() webView.loadUrl("http://example.com") val webView: WebView = findViewById(R.id.webview) webView.webViewClient = WebViewClient() webView.loadUrl("http://example.com")
  4. How to create a WebView in Compose @Composable fun WebViewSample(

    url: String, webViewClient: WebViewClient = WebViewClient() ) { AndroidView( factory = { context -> WebView(context).apply { this.webViewClient = WebViewClient() } }, update = { webView -> webView.loadUrl("http://www.example.com") } ) }
  5. Opt-out of metrics collection • For users who share usage

    statistics and diagnostics with Google, WebView sends usage statistics and crash reports • Apps can opt-out of this <manifest> <application> <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" /> ... </application> </manifest>
  6. Remote Debugging • Never enable it for non-debug builds (it’s

    disabled by default)! • It enables interaction and arbitrary JavaScript execution within the WebView content via Chrome Developer Tools • developer.chrome.com/docs/devtools/remote-debugging • developer.chrome.com/docs/devtools/console/javascript chrome://inspect#devices
  7. Breakout • If you don’t restrict the URLs that can

    be loaded, navigation to a search engine (e.g., Google) may be possible • From there any website can be loaded • More dangerous when combined with other vulnerabilities (e.g., enabled debugging or clear text traffic) and attacks (e.g., MitM, protocol downgrade) that exploit them • Especially dangerous if JavaScript execution is enabled
  8. Clear Text Traffic • Even though Network Security Config is

    available since Android 7 (API 24), WebViews only respect it since Android 8 (API 25) • Even if you disabled clear text traffic, WebViews might still allow it! • You need to manually prevent loading URLs with http protocol object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean { if ("http" == request?.url?.scheme) { return true } ... } }
  9. HTTP Strict Transport Security (HSTS) • HSTS helps to protect

    websites against man-in-the-middle attacks such as protocol downgrade and cookie hijacking • The best is to not allow clear text traffic at all • But if you really need to, you should at least enforce HSTS in Network Security Config <network-security-config> <base-config hstsEnforced="true"> ... </base-config> ... </network-security-config>
  10. Opaque Origin • When displaying local web content, setting the

    base URL is often forgotten • No surprise as the default loadData method doesn’t allow it you should not use that method • Same Origin Checks would thus fail (when e.g., navigating to a web URL) it is tempting to just disable them BUT DON’T DO THAT • Instead, the newer loadDataWithBaseUrl should be used • Additional benefit is that it accepts more types of encoding (including UTF-8) DON’T webView.loadData(Base64.encode(data.toByteArray()), "text/html", "base64")
  11. Opaque Origin • When displaying local web content, setting the

    base URL is often forgotten • No surprise as the default loadData method doesn’t allow it you should not use that method • Same Origin Checks would thus fail (when e.g., navigating to a web URL) it is tempting to just disable them BUT DON’T DO THAT • Instead, the newer loadDataWithBaseUrl should be used • Additional benefit is that it accepts more types of encoding (including UTF-8) DO webView.loadDataWithBaseURL("https://www.example.com", data, "text/html", "UTF-8", historyUrl)
  12. Safe Browsing • Google Safe Browsing is a service that

    lets client applications check URLs against Google's constantly updated lists of unsafe web resources • Enabled by default since Android 8 (API 26) and WebView v66 • You can also explicitly enable it <manifest> <application> <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="true" /> ... </application> </manifest>
  13. File Access • Allowed by default up to Android 10

    (API 29) • Of course, READ_EXTERNAL_STORAGE permission is needed for the app to use it • Further complicated with Scoped Storage from Android 11 (API 30, opt out possible) and 12 (API 31, no opt-out) • Still quite easy to access Media files and files that were created by the app webView.settings.allowFileAccess = false
  14. Content Provider Access • Still allowed by default • On

    all Android versions! • Data published by your Content Providers can be accessed by their URI webView.settings.allowContentAccess = false
  15. Cookies in WebViews • Not clearing cookies can be a

    problem • Cookie jar is shared within the same process, you cannot clear cookies for individual WebViews (unless they run in their own process) • Don’t forget evercookies • github.com/samyk/evercookie
  16. androidx.webkit • Backport of newer android.webkit APIs to older versions

    • Classes: WebViewCompat, WebSettingsCompat, etc. • Features: Dark mode, overriding user agent, multi-profile, etc. • developer.android.com/reference/androidx/webkit/package- summary.html • developer.android.com/jetpack/androidx/releases/webkit
  17. Running JavaScript • JavaScript rendering runs in a separate process

    from Android 8 (API 26) • On older versions it can access the memory of the application • By default, loadUrl can run JavaScript code you should not use it • The dedicated method instead is evaluateJavaScript • You get the result in a callback • It also can persist JavaScript objects between navigation DON’T webView.loadUrl("javascript:void(alert(\"Hi!\"))")
  18. Running JavaScript • JavaScript rendering runs in a separate process

    from Android 8 (API 26) • On older versions it can access the memory of the application • By default, loadUrl can run JavaScript code you should not use it • The dedicated method instead is evaluateJavaScript • You get the result in a callback • It also can persist JavaScript objects between navigation DO webView.evaluateJavascript("javascript:void(alert(\"Hi!\"))", resultCallback)
  19. Running JavaScript • If you accept (user) input (from untrusted

    sources) you should always escape/sanitize them to avoid code injection • E.g., basic escaping via StringEscapeUtils.escapeEcmaScript from Apache Commons Text • github.com/apache/commons-text • Or more robust with OWASP Enterprise Security API (ESAPI) • owasp.org/www-project-enterprise-security-api
  20. Running JavaScript • If you need to interact with native

    code from JavaScript, be very careful how you use the Javascript Bridge • Use the @JavascriptInterface annotation that prevents unauthorized method access (through reflection), limiting the attack surface • Potential exploitation for instance, via an XSS attack, that enables the calling of exposed Java methods • To mitigate risks, restrict JavaScript bridge usage to code shipped with the APK and prevent loading JavaScript from remote sources
  21. JavaScript Engine • Available on Android 8 (API 26) and

    above • Still Beta • Provides a way to evaluate JavaScript code without creating a WebView instance • Can be done in a Service (WorkManager task) • Still uses a WebView behind the scenes The WebView installed on device needs to support it • Multiple isolated environments with low overhead, enabling the application to run several JavaScript snippets simultaneously • Cannot make network requests developer.android.com/develop/ui/views/layout/webapps/jsengine
  22. Custom Tabs • Originally a Google Chrome feature, since then

    it formed into a standard, implemented by most modern browsers on Android • developer.android.com/jetpack/androidx/releases/browser • An instance of the browser runs in the task of the caller app • Shares cookie jar with the real browser • Security is the responsibility of the browser, web pages have no access to your app • Customizable look and feel
  23. Thank you! • Modern WebView best practices (Android Dev Summit

    ‘18) • youtu.be/HGZYtDZhOEQ • Android Security Evolution • balazsgerlei.com/AndroidSecurityEvolution • Executing JavaScript and WebAssembly • developer.android.com/develop/ui/views/layout/webapps/jsengine • Overview of Android Custom Tabs • developer.chrome.com/docs/android/custom-tabs/ • HackTricks - Webview Attacks • book.hacktricks.xyz/mobile-pentesting/android-app-pentesting/webview-attacks Balázs Gerlei @balazsgerlei speakerdeck.com/balazsgerlei
  24. Thank you! • Modern WebView best practices (Android Dev Summit

    ‘18) • youtu.be/HGZYtDZhOEQ • Android Security Evolution • balazsgerlei.com/AndroidSecurityEvolution • Executing JavaScript and WebAssembly • developer.android.com/develop/ui/views/layout/webapps/jsengine • Overview of Android Custom Tabs • developer.chrome.com/docs/android/custom-tabs/ • HackTricks - Webview Attacks • book.hacktricks.xyz/mobile-pentesting/android-app-pentesting/webview-attacks @mastodon.social speakerdeck.com/balazsgerlei @balazsgerlei