Android WebView: Secure Coding Practices
This is part one of a three-part series. Click here to read part two.
There is no doubt that mobile applications have changed the world in a big way. Just look at interaction habits, for example, the way people socialize as individuals or in a group has changed as what was once far away is now at our fingertips. There is an infinite number of applications and resources available to millions of users. And as these numbers grow, security concerns raise as well.
Everything nowadays is all about connectivity and delivering a great, secure and transparent browsing capability. With this in mind, web-based content can be embedded into Android applications using a resource called WebView. Using this component brings many functionalities, but it can also be a huge risk. That is why assuring WebView secure coding is so important.
WebView is a system component that allows Android apps to display content from the web directly inside an application, creating the concept of hybrid apps. An app can be Native, Hybrid, or Web (HTML5).
A Native app has to be installed on the device, as the Web app runs on the browser with no need for installation. The Native approach will use the operating system's supported language, which in Android's case is Java (Objective-C/Swift in iOS systems, C# in most Windows Phones), and the Web approach will use HTML5, JavaScript, and CSS.
The WebView allows a Hybrid approach, the creation of Native apps that load local or external web content. This combines the strength of Native apps (more performance and functionality) with the strength of Web apps (portability) and is the reason why many companies are using it today.
Until Android 4.3 (Jelly Bean), the WebView was based on WebKit and had some security and performance issues. This caused manufacturers, including Samsung and HTC, to replace the default WebView with a modern version of WebKit or Chromium (open source version of Chrome with Blink and V8 JavaScript engine) which is more stable and supports modern HTML5 features. Since the release of Android 5.0 (Lollipop), WebView has been on an APK and can be updated separately from the operating system.
The focus of this guide is to provide ground programming guidelines that can minimize the risk of developing a vulnerable app. The WebView component has been strengthened over the years but can still be easily abused if no security measures are followed during implementation.
Loading Clear-Text Content
Loading Clear Text Content (LCTC) consists of loading web content without encrypting traffic, making it vulnerable to Man-in-the-Middle (MitM) attacks. This kind of attack can lead to the leaking of sensitive information or traffic manipulation.
If credentials are obtained during a MitM attack, the attacker can then assume the victim's identity for that specific website. In addition to other information that may be gathered (such as the email address), the consequences can be much worse as it is well known that users reutilize the same password for several different services.
In the following image, it is possible to see an example of cleartext content loaded from a login web page where credentials are easily intercepted:
Without enforcing encryption through SSL/TLS, the attacker can also make changes to the intercepted traffic to, for instance, inject a keylogger in the HTTP response and then gather much more information from the user.
The Java code in this example is shown below. The use of the HTTP protocol should be replaced by HTTPS. Using SSL/TLS requires a properly signed certificate installed on the web server but it is a completely worth while implementation.
Vulnerable Code:
Keep in mind that since API 23 (Marshmallow) there is the "android:usesCleartextTraffic" flag that can be set to false on the Manifest to prevent clear text content from being loaded. Its default value is true.
SSL Error Handling
As we previously said, improper SSL Error Handling can lead to severe security breaches. By default, a WebView will not load web content if errors are detected during the SSL/TLS negotiation. The most common scenario in which these errors happen is when the server certificate is not signed by a recognized authority.
Instead of obtaining a properly signed certificate, many development companies choose to implement bypassing mechanisms that ignore certificate errors. With this approach, there is no need for a valid certificate but the application becomes vulnerable to Man in the Middle (MitM) attacks, presenting the same risk as the situation described in the section above. With the help of a homemade invalid certificate, the attacker is able to intercept and manipulate traffic.
In the following example, a WebView was implemented to load the webpage https://github.com. A MitM attack was performed and the result in the victim device is an empty screen because the WebView detected the invalid certificate and didn't establish the connection to the GitHub server.
The following error is displayed on the debugger:
If a bypassing mechanism to SSL errors is implemented, the attacker is able to fulfill the attack. This is shown in the following image where traffic interception and HTML code injection were performed by the attacker after the bypassing mechanism was implemented. The GitHub page is now loaded and the injected code is executed.
As mentioned before, the default behavior is to block improper SSL/TLS connections. Obtaining and installing a certificate signed by a recognized authority should always be the approach to take as it is the only secure option.
Enabling JavaScript
JavaScript execution is disabled by default on WebViews. This behavior is changed with the "setJavaScriptEnabled" function and the first recommendation is to maintain the default behavior if there is no need for client-side scripting. This way, the app becomes resilient to Cross-Site Scripting (XSS) attacks and reduces the consequences of a Man in the Middle (MitM) attack.
A common attack vector for mobile apps is ads. Advertisements from external sources are often loaded in WebViews and blocking JavaScript execution is a good way to prevent malicious code from being injected and protect the app users.
In a scenario where JavaScript is mandatory, all inputs should be sanitized to prevent XSS attacks. Other measures must be implemented to strengthen the application and will be described later in this blog post.
To fully understand the danger of JavaScript execution and its direct relation to XSS attacks, a small application was developed. It is a game that asks the user's name at the beginning. By the end of the game, a web page is loaded from an external server in a WebView, presenting a scoreboard with the overall classification of all the players. This allows players from different devices to compete. In the following images, you see the WebView presenting the scoreboard and some of the HTML/JavaScript code.
The player's name is printed on the scoreboard because the setJavaScriptEnabled
method was called with the argument "true." If this property is set to false, as it's possible to observe in the next piece of code, the WebView doesn't execute the JavaScript code and the player's name is not shown.
If JavaScriptEnabled
is set to "true" again, this application is vulnerable to XSS. At the beginning of the game, the user provides his name and this input is not properly sanitized.
So, if the value "Player's name<script>window.location.href='http://evilpage.com';</script>" is introduced at the end of the game, the scoreboard WebView will render this injected malicious code and instead of showing the scoreboard, it will show the contents of evilpage.com, as you can see in the following image.
XSS can lead to very severe consequences. In conclusion, disable JavaScript if possible.
Accessing Local Resources
It is possible for a WebView to access local resources by default, although some restrictions are enforced. These restrictions may vary with the API being used. Here is a list of methods that can be used to change the WebView default permissions:
- setAllowContentAccess - its default value is "true" and allows the WebView to access content providers. Content providers are usually created to allow secure data sharing between applications.
- setAllowFileAccess - its default value is "true" and allows the WebView to load content from the filesystem. For this, a "file:///" schema is used.
- setAllowFileAccessFromFileURLs - its default value is "false" since API 16 (Jelly Bean), before this version was "true" by default. It allows JavaScript from local web pages that are rendered inside WebViews and called with a "file:///" schema to access resources on the filesystem. This setting is ignored if the method
setAllowUniversalAccessFromFileURLs()
is called with the parameter "true." - setAllowUniversalAccessFromFileURLs - its default value is "false" since API 16 (Jelly Bean); before this version was "true" by default. It allows JavaScript from local web pages that are rendered inside WebViews and called with a "file:///" schema to access resources from any origin.
As described above, the WebView can load local resources by default and the "file:///" schema must be used with caution because it may lead to unauthorized file access.
For awareness, a small vulnerable app was prepared and a successful attack is described next. The vulnerable app is a game and uses a WebView to display the player's avatar, relying on the following code:
If it is possible to manipulate the player's name in the application, controlling the result of the showPlayerName
function, allowing other files to be obtained from the local system instead of the user's avatar. If an attacker controls the showPlayerName
function in a way that it returns the value "../../../storage/emulated/0/Pictures/pic001.jpg," an image from the smartphone photo gallery is displayed in the WebView, as it is shown below:
Avoid allowing the WebView to access local files in a dynamic way. If a static implementation is not possible, analyze the option of keeping the default "false" value in the methods setAllowFileAccessFromFileURLs
and setAllowUniversalAccessFromFileURLs
.
Using JavaScript Interfaces
JavascriptInterfaces allow JavaScript code rendered in the WebView to call Java methods implemented in the app. This is a very powerful feature as it allows web pages in a WebView to interact with all the device features, such as the camera, microphone, or the SMS manager.
Despite bringing outstanding features, they are also considered a huge security risk. This is because the attacks referred to in the sections above can have much more impact, if succeeded, by interacting with the app code.
Until API 17 (Jelly Bean - Android 4.2), JavascriptInterfaces were always directly associated with critical risk levels because of a code execution vulnerability (CVE-2012-6636). They weren't properly restricted and allowed the execution of arbitrary methods of Java objects, using the Java Reflection API within crafted JavaScript code loaded in a WebView.
From API 17 up until the most recent versions, an annotation must be declared on the exposed native functions with "@JavascriptInterface," and only those annotated methods will be exposed to the JavaScript code.
Due to this security implementation, it is recommended to compile applications against Android API level 17 or above. By forcing the addition of the "@JavascriptInterface" annotation, it prevents access to operating system commands via java.lang.Runtime.
Keep in mind that JavascriptInterfaces may allow Cross-Site Scripting (XSS) and Man in the Middle (MitM) attacks to reach the application's exposed methods. Additional security measures must be implemented in these methods to prevent damage in case they are called from malicious code.
Validating Content From Third-Parties
Knowing that WebViews are commonly vulnerable to Cross-site Scripting (XSS) and Man in the Middle (MitM) attacks it is advised to implement additional security restrictions, providing a safer environment to the application's users.
Validating the origin of the content being loaded by the WebView is a good security precaution. It can be implemented by overriding the shouldOverrideUrlLoading
and the shouldInterceptRequest
methods.
The shouldOverrideUrlLoading
is related to the opening of new web pages by the WebView. The shouldInterceptRequest
method is more intrusive, allowing the control of every resource accessed by a web page loaded in a WebView.
To demonstrate the effectiveness of implementing one of these security measures, the example given earlier (Enabling JavaScript) can be used. It consists of a successful XSS attack that redirects the WebView to an evil page. This redirection can be blocked by overriding the shouldOverrideUrlLoading
method, as it is demonstrated in the following image.
The code shown above restricts the WebView of this app to only load web pages from the Checkmarx domain. Notice that the original URL "http://10.0.0.2" will be loaded correctly. However, if the XSS attack from earlier (Enabling JavaScript) is performed, the evilpage.com URL is replaced by the contact us Checkmarx page. This is demonstrated in the following image.
This is a very effective way to secure the WebView. Additional features can be added, such as an alert in case of attack detection.
This is part one of a three-part series. Click here to read part two.
References
Attacks on WebView in the Android system, 2011;
Automatically Detecting SSL Error-Handling Vulnerabilities in Hybrid Mobile Web Apps, 2015
Bulletproof Android: Practical Advice for Building Secure Apps, 2014
Cross-site Scripting Attacks on Android Hybrid Applications, 2017;
Do not allow WebView to access sensitive local resource through file scheme: https://www.securecoding.cert.org/confluence/display/android/DRD02-J.+Do+not+allow+WebView+to+access+sensitive+local+resource+through+file+scheme;
Draco: A System for Uniform and Fine-grained Access Control for Web Code on Android, 2016;
https://www.cvedetails.com/cve/CVE-2014-6041/;
Secure Integration of Web Content and Applications on Commodity Mobile Operating Systems, 2017;
The Mobile Application Hacker's Handbook, 2015;
Webview selection from user access patterns, 2007;
WebView addJavascriptInterface Remote Code Execution: https://labs.mwrinfosecurity.com/blog/webview-addjavascriptinterface-remote-code-execution/;
Attacks on Android WebViews: http://resources.infosecinstitute.com/android-hacking-security-part-7-attacks-android-webviews/;
WebView Vulnerabilities in Android Applications: http://scrub.cs.berkeley.edu/wp-content/uploads/2013/05/WebViewsSCRUBPresentation.pdf;
Follow WebView Best Practices: https://github.com/nowsecure/secure-mobile-development/blob/master/en/android/webview-best-practices.md
Android Security Tips: https://developer.android.com/training/articles/security-tips.html
Adventures with Android WebViews: https://labs.mwrinfosecurity.com/blog/adventures-with-android-webviews/
Android WebViews and the JavaScript to Java Bridge: https://www.synopsys.com/blogs/software-security/android-webviews-and-javascript-to-java-bridge/
Android WebKit: https://developer.android.com/reference/android/webkit/package-summary.html
Android WebView: https://developer.android.com/reference/android/webkit/WebView.html
High Performance Mobile Web, 2016;
What is a hybrid mobile app: http://developer.telerik.com/featured/what-is-a-hybrid-mobile-app/
Android WebKit WebSettings: https://developer.android.com/reference/android/webkit/WebSettings.html
CVE-2012-6636: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6636
Securing WebViews in Android Applications: https://datatheorem.github.io/2014/03/21/securing-webviews-android/
Secure Usage of Android WebView: http://blog.opensecurityresearch.com/2014/04/secure-usage-of-android-webview.html
Penetration Testing Lab: https://pentestlab.blog/2017/02/12/android-webview-vulnerabilities;