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
Related
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.
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.
For example, user is navigating to google.com in WebView.
Is it possible to authorize him there via Google Account Picker (something like described here https://developers.google.com/android/guides/http-auth) to simplify authorization instead of manually logging in via web form?
Android Web browsers (for example, Google Chrome) are authorizing user via this method).
Part I: Using the Google Plus Services API
If I understand your question correctly, you may be able to achieve what you are trying to do using the Google Plus Services API.
You create your GoogleSignInOptions and then create your GoogleApiClient using these sign-in options. From there, you use the Auth.GoogleSignInApi.getSignInIntent with your GoogleApiClient as the parameter.
This intent should launch a SignInIntent that presents the Google account picker (that will include accounts that have been accessed on the device previously, and the ability to add another account).
Once you get back the GoogleSignInResult, you can verify that the user was authenticated and then create the authentication flow as you would otherwise.
Even included in the Android SDK is the Google SignInButton, which you can use right in your layout instead of having to create a custom button for the sign-in.
Part II: Using WebViewClient
Now, if you are trying to use a WebView to authenticate them, your best bet is to extend the WebViewClient class.
Things you will need: clientId, clientSecret, and clientScope (all of these details will be given for you when you create your application in the Google Developer Console)
First things first, your URL to authorize will probably be as follows: https://accounts.google.com/o/oauth2/auth?response_type=code&clientId={your client id}&state={SOMESTATEINFO}&access_type=offline (access type if you want offline access). This should be the initial URL of your WebView
Next, you will want to modify your extended WebViewClient class. What you will want to do is override the shouldOverrideUrlLoading(WebView webView, String url) method to listen for your redirectURL. Probably the easiest thing to do is to use url.startsWith(<your redirect URL>) to detect this. You can then parse the response. If your response contains error, then it means something went wrong. Otherwise, you should get back two fields in the URL: code and state. If you do not get error back, then return true for shouldOverrideUrlLoading.
Once you get your code, you can create a new GoogleAuthorizationCodeFlow, using your client, scopes, and secrets.
Once you have your flow, you will need a GoogleTokenResponse, which you will be able to get using the code obtained above for your authorization code, using GoogleTokenResponse response = flow.newTokenResponse(<code>).setRedirectUri(<redirectUri>).execute().
Once you have done this, and you have your response, you can get your Credential using flow.createAndStoreCredential(response, null).
And voila, using this Credential, you can authenticate your calls.
Caveats I have not been able to get the WebView to recognize accounts that have been signed into on other web browsers, so the account picker may only show the accounts that have been signed into on the app-specific WebView.
tl;dr It is possible to do this with a WebView and WebViewClient, but it's messy and a little bit more roundabout than using the Google Plus Services API.
This example better illustrates the authorization flow/credential stuff once you get the authorization code and such.
And here's some documentation on the WebViewClient that may be useful as well.
Hope this helps point you in the right direction!
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)
I created my first Sencha touch 2 app by watching this video (http://youtu.be/5F7Gx0-W-M4) and it has a store page structured like this:
Ext.define('FirstApp.store.Places',{
extend:'Ext.data.Store',
config:{
autoLoad:true,
model:'FirstApp.model.Place',
proxy:{
type:'ajax',
url:'https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AIzaSyCFWZSKDslql5GZR0OJlVcgoQJP1UKgZ5U',
reader:{
type:'json',
rootProperty:'results'
}
}
}
})
The after-build (after running "sencha app build native") package work very well on my MAC (all browsers) but the generated app (i am running it on my nexus phone) works but doesn't collect any data from the google maps json.
Any help would be appreciated
The example you are referring is using google map's place search API. You can not use this API when you build the app for mobile phone with proxy set to ajax . Basically, you can not use any resource that is outside your domain. Like if your site is at yourdomain.com and there is someotherdomain.com, then you can't make ajax request to this someotherdomain.com from yourdomain.com unless that domain allows you to. In this case, your mobile app is not having any domain. You are just loading a page inside webview.
The reason is, ajax will not be able to load cross-origin resources. App build works on browsers because I believe you're using chrome with --disable-web-security flag. To work with CORS you need to use JsonP proxy. It's the only way if you're packaging for mobile app. If in a case, you own the server ( not in this context though ) then you can allow CORS by setting appropriate headers like
Access-Control-Allow-Origin: *
or
Access-Control-Allow-Origin: http://yourdomain.com/resource
Try setting proxy to JsonP .