Blog

Android WebView: Are Secure Coding Practices Being Followed?

7 min.

December 20, 2018

WebViews are very common on the Android applications. There are clear WebView security best practices, but are they being implemented? With our previous blog post in mind, Kotlin Guide: Secure Coding Practices, we wanted to understand how security best practices in WebViews are being implemented in the wild. Are the apps with WebViews, currently available on the Internet, secure?

Methodology to Find Influential Projects

Our security research team performed a GitHub search to get a list of projects using WebView that are considered the most influential. To find the most influential projects, we used this formula to create a score = 0.7 * stars + 0.3 * forks, resulting in a set of 142 popular applications that implement WebViews and are potentially vulnerable.
Next we carefully analyzed this set of applications. Here are our conclusions.

Digging In: Projects Using Android WebView

We start with analyzing encryption issues. This graph illustrates the results for lack of HTTPS in communications:
Lack of Encryption in the Communications
More than half of the applications don’t enforce HTTPS in all communications. The usual excuses for not using HTTPS include the impact on performance and non-sensitive information that doesn’t need to be protected. However, these are not valid justifications anymore. A good example is the recent vulnerabilities disclosed for Tinder by the Checkmarx Research Team, which compromised user confidentiality. These vulnerabilities occurred due to the lack of SSL/TLS. These days, HTTPS should be enforced in all communications.

Mind the SSL/TLS Errors

Ignoring SSL/TLS errors is another common bad practice. When an HTTPS communication takes place, a negotiation for secure communication occurs, and errors may occur. For example, when an attacker launches a Man-in-the-Middle (MitM) attack against HTTPS connections, the SSL/TLS negotiation results in an error because a recognized authority did not sign the certificate of the server.
The default behavior for a WebView is to block the loading of content that results in an SSL/TLS error. However, it’s possible to program the application to bypass these errors. The following graph shows the percentage of projects that ignored SSL/TLS errors:
Improper SSL Error Handling
Only 8% of the analyzed applications have code to handle SSL/TLS errors in a customized way. Overall, 3% of the applications ignored HTTPS errors, which makes them vulnerable to MitM attacks.
As an example, below is vulnerable code extracted from one of the analyzed applications. The application should never ignore SSL/TLS errors.

@Override
      public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
        handler.proceed();
      }

Validating Content Loading

After encryption, the next security best practice in the analysis is to validate content loading. Android WebViews can validate whether the loaded content is what was excepted in the normal behavior of the application or if it was somehow manipulated. The next graph shows the results for apps that are vulnerable to manipulated content loading.
Lack of Content Loading Validation
In 4% of the overall applications, content is loaded on the WebView accordingly to parameters read from the loaded page. These parameters can be manipulated and lead to malicious code being loaded by the WebView.
To validate the URLs being loaded in the WebView, the shouldOverrideUrlLoading or the shouldInterceptRequest methods should be overridden. Besides validating URLs being loaded, parameters should always be validated as well. The following example shows an extracted insecure piece of code, where input validation should be in place.

private void loadArticle (@NonNull final HackerNewsItem item) {
      mWebView = new CacheableWebView(mContext);
      mWebView.setWebViewClient(new AdBlockWebViewClient(Preferences.adBlockEnabled(mContext)));
      mWebView.setWebChromeClient(new CacheableWebView.ArchiveClient() {
          @Override
          public void onProgressChanged(WebView view, int newProgress) {
              super.onProgressChanged(view, newProgress);
              notifyArticle(newProgress);
          }
      });
      notifyArticle(0);
      mWebView.loadUrl(item.getUrl());
  }

The previous code is called with:

if (mJob.articleEnabled && item.isStoryType() && !TextUtils.isEmpty(item.getUrl())) {
              if (Looper.myLooper() == Looper.getMainLooper()) {
                  loadArticle(item);
      ( … )

The “item” parameter is not being validated and the shouldOverrideUrlLoading/shouldInterceptRequest methods are not overridden to validate the loaded URLs.

Security for JavaScript in WebViews?

Regarding the use of JavaScript in WebViews, we can’t say that it is insecure. However, it certainly increases the likelihood of insecure coding, and that is why by default WebViews disable Javascript. We found JavaScript enabled in 58% of the analyzed applications:
JavaScript Enabled
Although not a bad practice by itself, we think it is an interesting finding to highlight.

Using JavascriptInterfaces in WebViews

Another similar development option is to use JavascriptInterfaces. These allow the WebView to use native Java functions that exist in the application code. Our team performed an analysis in search of vulnerable code, and the next graph shows the result:
Insecure JavaScript Interfaces
Although we found that 20% of the analyzed applications implement Javascript Interfaces, only 4% of the total amount were considered insecure. An insecure Javascript Interface can allow attackers to access the mobile device functionality and local resources, through XSS and MitM attacks. They should be protected by input validation and also a secure business logic.
We extracted the following piece of code from one of the analyzed applications to demonstrate the vulnerability:

@JavascriptInterface
	    public void openNativeCamera() {
	        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
	        mFragment.startActivityForResult(intent, G.ACTION_CAMERA);
	    }
@JavascriptInterface
	    public void sendMessage(String type, String phoneNumber, String message) {
	        if ("1".equals(type)) {
	            String SENT_SMS_ACTION = "SENT_SMS_ACTION";
	            Intent sentIntent = new Intent(SENT_SMS_ACTION);
	            PendingIntent sentPI = PendingIntent.getBroadcast(mContext, 0, sentIntent,
	                    0);
	            SmsManager smsManager = SmsManager.getDefault();
	            smsManager.sendTextMessage(phoneNumber, null, message, sentPI, null);
	        } else if ("2".equals(type)) {
	            Uri uri = Uri.parse("smsto:" + phoneNumber);
	            Intent sendIntent = new Intent(Intent.ACTION_VIEW, uri);
	            sendIntent.putExtra("sms_body", message);
	            mContext.startActivity(sendIntent);
	        }
	    }
	   @JavascriptInterface
	    public void openRecorder() {
	        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
	        intent.setType("audio/amr");
	        intent.setClassName("com.android.soundrecorder", "com.android.soundrecorder.SoundRecorder");
	        mFragment.startActivityForResult(intent, G.ACTION_RECORDER);
	    }

Conclusions on Android WebView Secure Coding Practices

We were disappointed with the final results. They show that there is still a lack of engagement from the development teams to guarantee that new applications are secure. We found the encryption of traffic the most unconsidered security practice. This bad practice is present in over 50% of the analyzed applications and jeopardizes users’ privacy—and the integrity of the software. Nowadays, there simply isn’t any justification for the use of HTTP over HTTPS, and the decision to use HTTP is putting users at risk.