Google has asked me to address https://support.google.com/faqs/answer/9095419 in my Android app, which basically means not to use the JavaScript injection mechanism for a web page loaded via HTTP.
Not using this mechanism (option 1) doesn't work for me. Setting android:usesCleartextTraffic to false also doesn't work, as the app uses non-HTTPS traffic elsewhere. So that leaves me with "you can ensure that any affected WebViews do not load any URLs with HTTP schemes via loadUrl" - which I'm happy to do, as my app only uses file:/// URLs to load content into the WebView, which should be fine security-wise. But how do I need to code the shouldOverrideUrlLoading method so that Google's checker recognizes that I'm using only file:/// URLs?
Note that the question is different from both Remediation for JavaScript Interface Injection Vulnerability (because I'm clear what is being asked) and In Android, JavaScript Interface Injection Vulnerability (because I'm not using HTTP, but file:/// URLs).
Edit: Adding my shouldOverrideUrlLoading method. (This isn't the entire method, but the salient part of it.)
#Override
public boolean shouldOverrideUrlLoading (WebView browser, String url) {
if (url.startsWith("file:///")) {
// This is my web site, so do not override; let my WebView load the page
browser.loadUrl(url);
return true;
}
// Otherwise, the link is not for a page on my site, or is an entirely different kind of URI
// (like tel:, geo: or mailto:), so launch another Activity that handles URLs
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
I have not found a way to use file:// URLs with assets in a way that satisfies Google code checker. While this would solve the issue, I'm still not clear how one might need to code it.
What I ended up doing -which solves my immediate problem- is to call a JavaScript method via the WebView.evaluateJavascript method. When called from within WebViewClient.onPageFinished the page has finished loading, so all elements are accessible. While not important for my case, this method can also return a value to the Java code. So while it's not a general replacement for a JavascriptInterface, it addresses some of its uses cases.
Related
we recently received a request as following -
While loading a webapp (https://192.1.../poc/test.jsp) in android or iOS webview. It's taking some time while loading 1st time because of js, image, css etc files takes time to download including some APIs.
Client want to improve the experience and wanted 1st time even takes time and load the .js, images etc offline. When user launch app 2nd time onwards, use the loaded .js, images etc and make only API call from server.
We would like to check if what is the best way to achieving it?
Any out of frame suggestion is also appreciated.
While digging too much, it was solved as following
iOS having in-build property by WKWebView to store supporting web files like (js, css etc) and reuse if needed for further commmunication. Logic to clear cache etc we can manage it manually when it needed.
Android it was webviewclient. Which is having a callback name shouldInterceptRequest(WebView view, WebResourceRequest request)
Everytime any webview page needs css, js etc, this callback to be invoked. So put the logic be like
if (if url extension is js|png|css){
if (response for url is available within app cache) {
return WebResouceResposne object from cache data
} else {
//Save file response in cache &
return to super.shouldInterceptRequest() using interceptor WebResourceResponse
}
}
Hope, it will help someone in anyways. Thank you!
I received a warning from Google Play Console that refers me to this page because I used JavaScript Interface in my app and suggest two options to solve the problem .
Option 1 tells :
Ensure that there are no objects added to the JavaScript interface of
any WebView that loads untrusted web content. You can do this in two
ways:
Ensure that no objects are ever added to the JavaScript interface
via calls to addJavascriptInterface.
Remove objects from the JavaScript interface in shouldInterceptRequest
via removeJavascriptInterface before untrusted content is loaded by
the WebView.
but I can't understand what google exactly says specially on :
Remove objects from the JavaScript interface in shouldInterceptRequest
via removeJavascriptInterface before untrusted content is loaded by
the WebView
can someone tell me more explanation ?
You can resolve this issue in following ways:
If your website supports HTTPS, use "https://" prefix in loadUrl method.
You can set android:usesCleartextTraffic to false in your Manifest or set a Network Security Config that disallows HTTP traffic. It also means that your website should run on HTTPS.
Now, coming to your question about "Remove objects from the JavaScript interface in shouldInterceptRequest via removeJavascriptInterface before untrusted content is loaded by the WebView" : It mean that your app should remove (or disable) JavaScriptInterface whenever there is any non HTTPS URL is loaded within the WebView.
After doing any of these, you need to update APK on Play Console.
Conclusion is that if you want to use JavaScriptInterface, better use HTTPS on your website. If you use HTTP, JavaScriptInterface won't be allowed by Google Play.
I faced the same problem, and have not been able to figure this out, either. What worked for me, documented in How to address "Remediation for JavaScript Interface Injection Vulnerability"?, was to use WebView.evaluateJavascript. Alas, that is not a full replacement for all use cases of JavascriptInterface, but maybe it's sufficient for your purposes.
I just release an update without doing something special and warning disappeared BUT not sure it will came back again or not
We have an android application that's iframe-ing our website into their application. However to prevent click jacking we have the following directive in our proxy configs.
Header append X-FRAME-OPTIONS "SAMEORIGIN"
This is a very common Cross-Origin Resource Sharing strategy.
Unfortunately the Webview in an android browser has the origin as file:// which is different than the domain we use. This leads to the error refused to display x-frame-options set to sameorigin.
What strategies (either on the proxy or the client side) Can I employ to allow the android application to interact with our site (without COMPLETELY removing sameorigin)?
Don’t think you can do that. Since Chromium doesn’t see Allow-From as feature[1] and Android relies heavily on Chromium’s frameworks for WebViews.
I’m guessing your requirements are to block browser based click jackings?
Since you can’t use Allow-From. You may want to think about an approach similar to that outlined in this BlackHat talk[2], UI Redressing Attacks on Android Devices. I’d recommend reading the entire pdf really interesting stuff.
Check out Chapter 5 MITIGATION TECHNIQUES, Section 1 Browser-Based UI Redressing.
<styleid=”antiClickjack”>
body{display:none!important;}
</style>
<scripttype=”text/javascript”>
if(self===top){
varantiClickjack=document.
getElementById(”antiClickjack”);
antiClickjack.parentNode.removeChild(antiClickjack);
}else{
top.location=self.location;
}
</script>
[1] https://code.google.com/p/chromium/issues/detail?id=129139#c20
[2] https://media.blackhat.com/ad-12/Niemietz/bh-ad-12-androidmarcus_niemietz-WP.pdf
The WebView has a loadDataWithBaseURL() method. You could read in your file, and pass that through with whatever origin you need as the base url.
public void loadDataWithBaseURL(String baseUrl,
String data,
String mimeType,
String encoding,
String historyUrl)
Loads the given data into this WebView, using baseUrl as the base URL for the content. The base URL is used both to resolve relative URLs and when applying JavaScript's same origin policy.
I need to catch malformed links (without "http://") in a webpage loaded by the app (through a WebView container) and format them correctly (adding "http://") which I do by Overriding
public boolean shouldOverrideUrlLoading(WebView view, String url).
if(url.startsWith("/")){
url = "http://www.domain.com" + url;
}
Everything was nice but I realize that in API 19 it fails to call shouldOverrideUrlLoading method according to this guide.
But they do not say which method gets to be call to decide if it is a valid or not valid url.
Solutions like get all web content from the page to load, find and replace the bad formatted links and then load that "new" page instead seems to much effort for something so little.
Does anyone knows what method to override (intercepting clicks from the WebView)?
Google has some documentation regarding shouldOverrideUrlLoading() in KitKat:
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For
example, if you implement callbacks such as shouldOverrideUrlLoading()
or shouldInterceptRequest(), then WebView invokes them only for valid
URLs.
If you are using a custom URL scheme or a base URL and notice that your app is receiving fewer calls to these callbacks or failing to
load resources on Android 4.4, ensure that the requests specify valid
URLs that conform to RFC 3986.
This might seem to be a weird problem, but I am curious to know if it would work. I am working on a POC, and hence have to either prove or disprove that this works or not.
The UI in the Android app would be native (Java + XML layouts) + some other device features access like (Camera/File system etc).
There is a JS library that I have built, that has a few functions which do Ajax post and get requests.
In the app, I have an invisible Webview, where I load a blank HTML (referencing this JS library). And into that WebView, I have injected a JavascripInterface. So, essentially, the UI would be native, and you would never see the Webview. That's just a host which provides access to my JS library to the native code.
Now, on some action on my UI, I call the JS functions on the Webview, which in turn tries to make an ajax call (loadUrl calls ex. javascipt:functionName()). But, those calls fail, without any visible errors.
Note: This same HTML file works, if I load it up on my desktop browser. The AJAX calls succeed.
But, when I initiate Ajax calls through the JavascriptInterface(or webview.loadUrl() calls), they fail, with a reponse status 0.
Things apart from AJAX, like simple function calls, alerts, and callbacks through javascript interface work fine though.
Q: I know this is a weird and an unpractical way to do things. But, would it/should it work?
Update: Even after setting the setBlockNetworkLoads(false), it still doesn't work.
I tried logging the JS calls and errors, and got this error.
Request header field X-Requested-With is not allowed by
Access-Control-Allow-Headers.
Any idea how to solve this?
It seems that your are trying to do a cross domain ajax request.
Cross domain requests are not allowed by same origin policy and so the requests will be blocked. If you are loading a local file in webView and then sending ajax requests from it to other domains, this will be the case.
You if that is the case and it is the same origin policy causing you trouble then you might want to look at Cross-origin Resource Sharing (CORS) or JSONP to workaround it.
Given the error you get it seems that your problem is similar to one discussed here:
Cross-Domain AJAX doesn't send X-Requested-With header
You might want to change server settings to allow X-Requested-With header.
Also it seems that from API level 16, webSettings added a method setAllowFileAccessFromFileURLs(). Setting this to true for the webView might solve the problem as well.
I had a similar issue where I was loading a "web-app" locally into a WebView, just doing Ajax remotely. I observed a similar problem where Javascript alerts etc worked fine, but AJAX calls didn't. It turned out that by default the WebView blocks "network loads".
Make sure you do this:
webView.getSettings().setBlockNetworkLoads(false);
That did it for me. Just to clarify, I wasn't using a Javascriptinterface - just loading a web-app as-is using webView.loadDataWithBaseUrl() - the baseUrl parameter passed to this method was where I perform all my AJAX calls (since this method respects the same origin policy)