Securing WebViews in Android Applications

A remote code execution vulnerability was discovered in Google Android 4.1 (and below) in early 2012. The root cause of the vulnerability is due to the way the addJavascriptInterface function exposed native methods to the JavaScript loaded on the WebView. If an application is vulnerable to this issue, a remote attacker can do several dangerous things, including the execution of malicious code, escalating privileges, extracting all user data, loading malware on the device, etc.

An application is vulnerable if the following conditions are true:

  • The application is compiled for API Level 16 and below. (Target API is 16 or Below)
  • The application uses WebViews and has JavaScript enabled.
  • The application defines an interface between your JavaScript code and client-side Android Code.

This vulnerability can be exploited by loading malicious JavaScript on the WebView. There are multiple entry points for this malicious JavaScript, including a few non-exhaustive examples below:

  • The application requests content from the server using any plain text (HTTP) communication.
  • The application loads third party content. The content could include Ads or content from partner websites.
  • You application has a persistent cross site scripting vulnerability

For a free scan of this issue of your Android app, simply contact hd@datatheorem.com at anytime

Below are the code level details to fix this vulnerability:

  • Compile with API level 17 and above - Enable bridge if the platform level is 17 and above.
  • Use shouldOverrideUrlLoading to allow trusted content
  • Prevent using JavaScript and addJavascriptInterface if it can be avoided.

Below are the steps to implement API level 17 and above for your application

The main reason the vulnerability existed in Android version prior to Android 4.2 was because an attacker could use java reflection and acquire a reference to the Runtime object through the exposed JavaScript Bridge. Google fixed this vulnerability in Android 4.2 (API Level 17). Now, any application built using API Level 17 and above must declare an annotation on the exposed native functions (@JavascriptInterface) and only those annotated methods would be exposed to the JavaScript code via the bridge. However, one must always express caution when exposing any native functionality to the JavaScript code.

Code for exposing native methods in WebViews in API Level 17 and above is shown below:

public class WebViewActivity extends Activity {

  private WebView WebView;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.WebView);
    Intent intent = getIntent();
    String url = intent.getStringExtra("url");
    WebView = (WebView) findViewById(R.id.WebView1);
    WebView.getSettings().setJavaScriptEnabled(true);
    WebView.addJavascriptInterface(new WebAppInterface(this), "DataTheoremJBridge");
    WebView.loadUrl(url);
  }

  public class WebAppInterface {
      Context mContext;

      /** Instantiate the interface and set the context */
      WebAppInterface(Context c) {
          mContext = c;
      }

      /** Show a toast from the web page */
      @JavascriptInterface
      public void showToast(String toast) {
          Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
      }
  }
}

To make sure the application is targeted for API Level 17, check the Android Properties Section in the IDE you are developing the project on. For the ADT (Eclipse) is should look like the screen shot below: Screenshot

Additionally, if you are using Eclipse you can verify the target property in the project.properties file. Also, make sure the element in the AndroidManifest.xml file is set as follows:

<uses-sdk android:minSdkVersion=“17" />
<uses-sdk android:targetSdkVersion=“17" />

The above AndroidManifest.xml file setting makes sure that your applications only runs on android devices running Android 4.2 and above. However, this is not a convenient option for many developers since about half of the devices present in the market do not fall in this sub set.

Another option that may be more feasible as it safely allows the application to run on a lower version of Android is shown below:

  • Make sure the application is targeted for API Level 17. This could be verified using the check described above.
  • Configure the element in the AndroidManifest.xml file as shown below.
<uses-sdk android:minSdkVersion=“xx" />
<uses-sdk android:targetSdkVersion=“17" />

The above configuration allows the application executing on devices running Android 4.2 and above to access the JavaScript Bridge functionality. When the same application is executing on devices running Android versions below 4.2 the JavaScript Bridge remains disabled and cannot be accessed by the JavaScript Code.

The second approach described below is more towards minimizing the risk rather than eradicating the issue. WebView’s in Android allow the application to control which URL should load on the WebView using the shouldOverrideUrlLoading method. One fact to note here is the initial URL loaded on the WebView using the loadUrl method is not intercepted by the shouldOverrideUrlLoading method however any subsequent URL’s loaded on the WebView that are initiated via the user navigation are intercepted by the shouldOverrideUrlLoading method.

An example of using the shouldOverrideUrlLoading method is given below :

WebViewClient MyWebViewClient = new WebViewClient()
    {
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
          if (Uri.parse(url).getHost().endsWith("datatheorem.com") &&
              (Uri.parse(url).getScheme().equalsIgnoreCase("https")))  {
              // This is a trusted site, so do not override; let my WebView load the page
              return false;
          }
          // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
          // This is by default the Android Browser.
          Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
          startActivity(intent);
          return true;
      }
    };

WebView.setWebViewClient(MyWebViewClient);

In the above code we make sure our WebView loads only datatheorem.com domain retrieved over SSL (HTTPS).

References

Pavan Walvekar - 21 Mar 2014 at 16:27