GithubHelp home page GithubHelp logo

balazsgerlei / securewebview Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 2.0 234 KB

Android WebView wrapper with secure defaults to avoid security issues caused by misconfiguring WebViews.

License: Creative Commons Zero v1.0 Universal

Java 100.00%

securewebview's People

Contributors

balazsgerlei avatar drathar avatar marazmarci avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

securewebview's Issues

🐞 loadUrlWithoutEscapingJavascript doesn't respect allow/block lists

After realizing that it's not enough to check the allow/block list when loading a URL in shouldOverrideUrlLoading as that's not called when loading a page is initiated via loadUrl, the same filtering was added to not allow loading URIs that are on the blocklist, or if a whitelist is present not on it.

Unfortunately the other loadUrl method called loadUrlWithoutEscapingJavascript (which differs in not blocking JavaScript execution by escaping it) was not modified so the same logic should be added there as well.

JavaScript escaping in loadUrl can be bypassed using percent-encoding

If you pass a malicious crafted URL containing percent-encoded characters to loadUrl(String url, boolean escapeJavascript), injecting JavaScript code becomes possible, despite the protective measures implemented in the library.

Since WebView::loadUrl expects a URI, it uses percent-encoding (URL encoding) to decode the URI, which makes it possible to add characters to the payload that would normally be escaped by StringEscapeUtils::escapeEcmaScript. The " character becomes %22 after URL encoding. If you use this instead of quotes, you can bypass the escaping.

See my PoC (branch):

JavaScriptInjectionFragment.kt:

private const val MALICIOUS_PARAMETER = "alert(%22Hello World%22)" // instead of "alert(\"Hello World\")"
private const val INJECTED_JAVASCRIPT = "javascript:void($MALICIOUS_PARAMETER)"

For this specific case, a possible fix could be to replace all occurrences of the % character with %25, but I think that would break lots of other legitimate use cases, like when the URI doesn't start with javascript:, for example: https://www.google.com/search?q=I%27m+blue

Escaping the whole javascript:method("param") string is also problematic, as it may not even contain user input (example: loadUrl("""javascript:alert("hello"))"""). Only the user input should be escaped, but currently, that could only be the caller's responsibility, since the SecureWebView library can't know which parts of the JS string are untrusted user inputs, and which were written by the developer.

Let's see some examples:

  • This is obviously vulnerable to JS injection: loadUrl("""javascript:alert("hello" + "$name")""")
  • This is still vulnerable to JS injection, because name can contain percent-encoded chars: loadUrl("""javascript:alert("hello" + "${StringEscapeUtils.escapeEcmaScript(name)}")""")
  • This is the current implementation, which is still vulnerable to JS injection, because name can contain percent-encoded chars:
    • loadUrl(StringEscapeUtils.escapeEcmaScript("""javascript:alert("hello" + "$name")""")
    • But it breaks legitimate* JavaScript calls: loadUrl(StringEscapeUtils.escapeEcmaScript("""javascript:alert("hello")"""), which will result in this exact String passed to loadUrl: javascript:alert(\"hello\"). It won't run, because \" causes a JS syntax error.

*: I think legitimate JavaScript calls should always happen through WebView::evaluateJavascript. I'm not aware of any reason why should loadUrl("javascript:...") be used instead of evaluateJavascript, besides API 19 minSdk support, but fortunately the library's minSdk version is 21. Based on these, I suggest prohibiting passing URIs starting with javascript: to loadUrl, and instead developing protective measures around the evaluateJavascript method.

I think the library could provide two things to prevent misusages of evaluateJavascript: 1.) Education in documentation or doc. comments πŸ˜„ 2.) Idea: a JavaScript method call builder, where the caller provides the method's name and the list of parameters to be passed, which all will be automatically escaped one by one. We could modify the evaluateJavascript method to receive a JavaScriptCall instance built by this builder. Also, we could add an evaluateJavascriptUnsafe method which replicates the behavior of the original evaluateJavascript method.

WDYT? πŸ™‚

πŸ₯”

πŸ’Ž Copilot and Gemini chat should be disallowed/blocked

Was able to breakout of the Secure WebView sample app by navigating through to Copilot on Microsoft, or to Gemini on Google.
From there (after logging in) on Copilot I was able to reach other search engines like Brave. On Gemini links are redacted from chat, but there is still a Google button that searches on Google and lets you open suggestion links to the search results page.

I think these AI chats should be added to the disallowed list and maybe some other popular ones that could be reachable through socials.

🐞 Crash in shouldBlockRequest caused by WebView method called not from the Main Thread

If you try to run the Breakout or the Opaque Origin demos with the Secure WebView toggle enabled, the app crashes with the following stack trace:

FATAL EXCEPTION: main
Process: dev.gerlot.securewebview.sample, PID: 10468
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'ThreadPoolForeg'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 2) {e57dea9} called on null, FYI main Looper is Looper (main, tid 2) {e57dea9})
	at android.webkit.WebView.checkThread(WebView.java:2627)
	at android.webkit.WebView.getSettings(WebView.java:1994)
	at dev.gerlot.securewebview.SecureWebView.shouldBlockRequest(SecureWebView.java:202)
	at dev.gerlot.securewebview.SecureWebView.-$$Nest$mshouldBlockRequest(Unknown Source:0)
	at dev.gerlot.securewebview.SecureWebView$SecureWebViewClient.shouldInterceptRequest(SecureWebView.java:138)
	at WV.J5.a(chromium-TrichromeWebViewGoogle6432.aab-stable-631211833:86)
	at org.chromium.android_webview.AwContentsBackgroundThreadClient.shouldInterceptRequestFromNative(chromium-TrichromeWebViewGoogle6432.aab-stable-631211833:15)
Caused by: java.lang.Throwable: A WebView method was called on thread 'ThreadPoolForeg'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 2) {e57dea9} called on null, FYI main Looper is Looper (main, tid 2) {e57dea9})
	at android.webkit.WebView.checkThread(WebView.java:2622)
	at android.webkit.WebView.getSettings(WebView.java:1994)Β 
	at dev.gerlot.securewebview.SecureWebView.shouldBlockRequest(SecureWebView.java:202)Β 
	at dev.gerlot.securewebview.SecureWebView.-$$Nest$mshouldBlockRequest(Unknown Source:0)Β 
	at dev.gerlot.securewebview.SecureWebView$SecureWebViewClient.shouldInterceptRequest(SecureWebView.java:138)Β 
	at WV.J5.a(chromium-TrichromeWebViewGoogle6432.aab-stable-631211833:86)Β 
	at org.chromium.android_webview.AwContentsBackgroundThreadClient.shouldInterceptRequestFromNative(chromium-TrichromeWebViewGoogle6432.aab-stable-631211833:15)Β 

πŸ’ŽImprove File Access demo by loading file from assets by default

Currently the File Access demo in the sample app would not work by default as it tries to load a file from the file system of the phone. While this is more demonstrative, it requires the user to copy a file to a specific folder from the assets of the app and if it would instead try to load directly from the assets folder, the demo would work "out of the box", which is preferrable.

The info description could still describe how to load a file from the file system, still encouraging the user to copy the image from the assets folder.

🐞 Erroneous comparison of hosts during host matching

In the code snippet of finding matching hosts there's no comparison of the member object and the parameter, but the member object gets compared to itself, which always produces a matching result.

case HOST -> { return this.uri.getHost() != null && this.uri.getHost().equals(this.uri.getHost()); }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.