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

Overcoming Unsecurities in WebViews // Android Budapest April 2024

Overcoming Unsecurities in WebViews // Android Budapest April 2024

My presentation from the Android Budapest April 2024 meetup about the insecurity you may feel due to unsecure WebViews.

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

Links
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

And finally my freshly released SecureWebView library:
https://github.com/balazsgerlei/SecureWebView

Balázs Gerlei

April 24, 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 again
  2. 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")
  3. 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") } ) }
  4. 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 ….
  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, a way can be found to a search engine (e.g., Google) • From there they reach basically any website • More dangerous when combined with other vulnerabilities (e.g., enabled debugging or plain text traffic) and attacks (e.g., MitM, protocol downgrade) that exploit them • Especially dangerous if JavaScript can be executed
  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 attacks 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. webView.loadData(Base64.encode(data.toByteArray()), "text/html", "base64") 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
  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) webView.loadDataWithBaseURL(“https://example.com", data, "text/html", "UTF-8", historyUrl) DO
  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 webView.loadUrl("javascript:void(alert(\"Hi!\"))") DON’T
  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 webView.evaluateJavascript("javascript:void(alert(\"Hi!\"))", resultCallback) DO
  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! @mastodon.social speakerdeck.com/balazsgerlei @balazsgerlei • 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