In the current Android native application development, in order to pursue development efficiency and porting convenience, using WebView as the main carrier for business content display and interaction is a good compromise. In such a hybrid app, it is inevitable that the page JS needs to call Java and call Java methods to perform functions that the web page JS cannot complete. The methods on the Internet can tell us that we can use addjavascriptInterface to inject native interfaces into JS at this time, but in systems below Android 4.2, this solution brings great security risks to our applications. If the attacker executes some illegal JS on the page (to induce users to open some phishing websites to enter the risky page), it is very likely to rebound and obtain the shell permissions of the user's mobile phone. Then the attacker can quietly install the Trojan in the background and completely penetrate the user's mobile phone. The detailed attack process can be seen in this report of Wuyun Platform: Interface hidden dangers in WebView and mobile phone Trojan use. For Android 4.2 and above (API >= 17), add @JavascriptInterface annotation to the callable method in the injection class. The method without annotation cannot be called. This method can prevent injection vulnerabilities. So is there a safe way that can fully take into account Android versions below 4.2? The answer is to use prompt, that is, the WebChromeClient input box pop-up mode. We refer to the solution given in the article Solution to Js Object Injection Vulnerability in Android WebView, but its JS method is a bit clumsy, the process of dynamically generating JS files is not clear, and the timing of loading JS files is not accurately grasped. So how can we modify it to conveniently call Java methods in JS code, and safely and reliably? The source code and project mentioned below can be found here Safe Java-JS Bridge In Android WebView [Github]. 1. Dynamically generate the JS code to be injected When constructing JsCallJava, it takes out the public and static methods of the class to be injected, generates the signatures of the methods one by one, caches the methods according to the method signatures, and dynamically generates a string to be injected into the webview by combining the method name with the static HostApp-JS code.
As can be seen above, the names of the various methods of the class are spliced into the two statically compressed JS codes. So what does the complete and clear HostApp-JS fragment generated in this way look like? Assuming that only three public static methods, toast, alert, and getIMSI, are defined in the HostJsScope class, the complete fragment is as follows:
In fact, when JsCallJava is initialized, we only splice the above line 15 hostApp.toast = hostApp.alert = hostApp.getIMSI = function () . The purpose is to graft all JS layer call functions into an anonymous function 1, and then use interception technology to traverse all functions under hostApp, take out the corresponding function name, and then graft all function calls under hostApp into another anonymous function 2. The purpose of this is to first execute anonymous function 2 when calling a function under hostApp, and anonymous function 2 will use the corresponding function name as the first parameter and then call anonymous function 1, so that anonymous function 1 can distinguish the call source during execution. A unified JS layer call entry and a unified return exit structure system are realized. 2. HostApp JS fragment injection timing Step 1 explains how to splice the HostApp-JS fragment. The JS fragment splicing is completed in the JsCallJava initialization, which is initiated when the InjectedChromeClient object is instantiated.
From the code in step 1, we know that the JS code spliced by JsCallJava is temporarily stored in the mPreloadInterfaceJS field. So when do we inject this code string into the page space of Webview? The answer is when the page loading progress changes.
From the above, we can see that the timing of injection is to accurately grasp when the progress is greater than 25%. If injected in OnPageFinished, the initial callback of the page document.ready will wait too long. We will talk about the detailed reasons later. 3. The process of page calling Java method execution OK, the above two steps solve the two major problems of dynamic generation and successful injection. Next, we need to deal with the specific JS calling process. As mentioned above, when the page calls the Java method, the anonymous js function prompts the json data after concatenating the parameters. The prompt message is intercepted by WebChromeClient.onJsPrompt in the Java layer.
The specific implementation of JsCallJava.call is as follows.
This is a complete parsing and matching process. It will generate a method signature again based on the method name and parameter type list passed in by the js layer, and match it with the method in the cache object that was initialized and constructed before. After a successful match, it will determine whether there is a number type in the js call parameter type. If there is, it will determine whether to take an int, long, or double type value based on the definition of the Java layer method. Finally, the call value list and method object are used for reflection execution, and the result of the function execution is returned. There are a few points to note here:
4. Use of HostApp on the page With the above preparations, we can now use HostApp in the page easily without loading any dependent files. For example, when clicking on the li tag:
But at the same time, there is a business scenario where the call should be triggered immediately when the page is initially loaded. If we write:
Then the call to HostApp is very likely to fail, because the timing of injecting the HostApp-JS fragment may be before or after document.ready. So how to solve this contradiction? If the HostApp JS has been injected successfully when document.ready is called, this is OK. If the HostApp JS has not been injected yet when document.ready is called, our JS script layer needs to be changed, that is, polling the status until the injection is successful or the timeout (1.5s) occurs, and then a callback occurs. The specific implementation is as follows (the following is an example of the modification of the $.ready() function of zepto.js).
This mechanism also explains why the JS injection of the Java layer is not placed in OnPageFinish. If that is the case, the number of page polling will increase, the waiting time will become longer, and there may be a timeout. Well, with the above changes, the call to HostApp needs to be triggered immediately when the page is initially loaded, as follows:
For more instructions and complete source code, see: Safe Java-JS Bridge In Android WebView [Github] Original: http://www.pedant.cn/2014/07/04/webview-js-java-interface-research/ |
<<: A different approach to building mobile apps: iOS and Android code sharing
>>: iPhone 6: The pain of mobile phone design revealed
Audit expert: Peng Guoqiu Deputy Chief Physician,...
Every summer, typhoons wreak havoc. The accompany...
E-commerce platform A has been established for ne...
Many people are familiar with contact lenses, and...
Arthur Eddington made outstanding contributions i...
As bidders, we may also be familiar with the term...
There is a drug that you may not have taken, but ...
[Kunming Men's Health Sauna Club], [Kunming S...
Recently, I often see complaints about Douyin in ...
Training course content: Guided by the concept, o...
[[141319]] Two weeks ago, I was working on a proj...
In the hot summer, eating a lychee is sweet and r...
Owls have fascinated humans for thousands of year...
Four major channels: official Taobao activities, ...
If the global chip shortage continues, spreading ...