Skip to content

MASTG-DEMO-0056: WebView Ignoring TLS Errors in onReceivedSslError

Download MASTG-DEMO-0056 APK Open MASTG-DEMO-0056 Folder Build MASTG-DEMO-0056 APK

Sample

This sample connects to https://tlsexpired.no, which has an expired SSL certificate, and demonstrates how a WebView ignores SSL/TLS errors by overriding the onReceivedSslError(...) method without proper validation. The app calls proceed() unconditionally, which allows the connection to continue even when there are TLS errors.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package org.owasp.mastestapp

import android.content.Context
import android.net.http.SslError
import android.util.Log
import android.webkit.SslErrorHandler
import android.webkit.WebView
import android.webkit.WebViewClient

class MastgTestWebView(private val context: Context) {

    fun mastgTest(webView: WebView) {
        webView.apply {
            webViewClient = object : WebViewClient() {
                override fun onReceivedSslError(
                    view: WebView,
                    handler: SslErrorHandler,
                    error: SslError
                ) {
                    val message = getSslErrorMessage(error)

                    Log.e("MastgTestWebView", "SSL errors onReceivedSslError: $message")
                    Log.e("MastgTestWebView", error.toString())

                    handler.proceed()
                }
            }
            loadUrl("https://tlsexpired.no")
        }
    }

    private fun getSslErrorMessage(error: SslError): String = when (error.primaryError) {
        SslError.SSL_UNTRUSTED      -> "The certificate authority is not trusted."
        SslError.SSL_EXPIRED        -> "The certificate has expired."
        SslError.SSL_IDMISMATCH     -> "The certificate Hostname mismatch."
        SslError.SSL_NOTYETVALID    -> "The certificate is not yet valid."
        SslError.SSL_DATE_INVALID   -> "The date of the certificate is invalid."
        else                        -> "SSL Certificate error."
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package org.owasp.mastestapp;

import android.content.Context;
import android.net.http.SslError;
import android.util.Log;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: MastgTestWebView.kt */
@Metadata(d1 = {"\u0000*\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0018\u0002\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u000e\u0010\u0006\u001a\u00020\u00072\u0006\u0010\b\u001a\u00020\tJ\u0010\u0010\n\u001a\u00020\u000b2\u0006\u0010\f\u001a\u00020\rH\u0002R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u000e"}, d2 = {"Lorg/owasp/mastestapp/MastgTestWebView;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "webView", "Landroid/webkit/WebView;", "getSslErrorMessage", "", "error", "Landroid/net/http/SslError;", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
/* loaded from: classes3.dex */
public final class MastgTestWebView {
    public static final int $stable = 8;
    private final Context context;

    public MastgTestWebView(Context context) {
        Intrinsics.checkNotNullParameter(context, "context");
        this.context = context;
    }

    public final void mastgTest(WebView webView) {
        Intrinsics.checkNotNullParameter(webView, "webView");
        webView.setWebViewClient(new WebViewClient() { // from class: org.owasp.mastestapp.MastgTestWebView$mastgTest$1$1
            @Override // android.webkit.WebViewClient
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                Intrinsics.checkNotNullParameter(view, "view");
                Intrinsics.checkNotNullParameter(handler, "handler");
                Intrinsics.checkNotNullParameter(error, "error");
                String message = this.this$0.getSslErrorMessage(error);
                Log.e("MastgTestWebView", "SSL errors onReceivedSslError: " + message);
                Log.e("MastgTestWebView", error.toString());
                handler.proceed();
            }
        });
        webView.loadUrl("https://tlsexpired.no");
    }

    /* JADX INFO: Access modifiers changed from: private */
    public final String getSslErrorMessage(SslError error) {
        switch (error.getPrimaryError()) {
            case 0:
                return "The certificate is not yet valid.";
            case 1:
                return "The certificate has expired.";
            case 2:
                return "The certificate Hostname mismatch.";
            case 3:
                return "The certificate authority is not trusted.";
            case 4:
                return "The date of the certificate is invalid.";
            default:
                return "SSL Certificate error.";
        }
    }
}

Steps

Let's run our semgrep rule against the sample code.

../../../../rules/mastg-android-network-onreceivedsslerror.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
rules:
  - id: mastg-android-network-onreceivedsslerror
    severity: WARNING
    languages:
      - java
    metadata:
      summary: This rule looks for the use of onReceivedSslError and ensures it throws an exception instead of silently muting TLS errors.
    message: Improper use of onReceivedSslError handler
    match:
      any:
        - public void onReceivedSslError(...) {...}
run.sh
1
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-network-onreceivedsslerror.yml ./MastgTestWebView_reversed.java --text --max-lines-per-finding 15 > output.txt

Observation

The rule identified one instance of the use of the onReceivedSslError(...) in the code.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
┌────────────────┐
 1 Code Finding 
└────────────────┘

    MastgTestWebView_reversed.java
    ❯❱ rules.mastg-android-network-onreceivedsslerror
          Improper use of onReceivedSslError handler

           27 @Override // android.webkit.WebViewClient
           28 public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
           29     Intrinsics.checkNotNullParameter(view, "view");
           30     Intrinsics.checkNotNullParameter(handler, "handler");
           31     Intrinsics.checkNotNullParameter(error, "error");
           32     String message = this.this$0.getSslErrorMessage(error);
           33     Log.e("MastgTestWebView", "SSL errors onReceivedSslError: " + message);
           34     Log.e("MastgTestWebView", error.toString());
           35     handler.proceed();
           36 }

Evaluation

The test fails because the app uses a WebView that calls proceed() in its onReceivedSslError(...) method without validating the TLS error at all. You can manually validate this in the app's reverse-engineered code by inspecting the provided code locations.

In this case:

            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                ...
                String message = this.this$0.getSslErrorMessage(error);
                Log.e("MastgTestWebView", "SSL errors onReceivedSslError: " + message);
                Log.e("MastgTestWebView", error.toString());
                handler.proceed();
            }

By doing this, the app is effectively ignoring every TLS error even though we can see that the expired certificate error is logged (see Monitoring System Logs):

logcat.txt
1
2
3
4
5
[ERROR:ssl_client_socket_impl.cc(877)] handshake failed; returned -1, SSL error code 1, net_error -201
SSL errors onReceivedSslError: The date of the certificate is invalid.
primary error: 4 certificate: Issued to: OU=Insubordinate,O=Insecure CA,1.2.840.113549.1.9.1=#161a696e73656375726540746c737265766f636174696f6e2e6f7267,C=NO,ST=Buskerud,CN=tlsexpired.no;
Issued by: 1.2.840.113549.1.9.1=#16126a6f68616e4073796473657465722e636f6d,CN=sydseter.com,OU=Sydseter,O=Sydseter,L=Drammen,ST=Drammen,C=NO;
 on URL: https://tlsexpired.no/