Skip to content

MASTG-DEMO-0054: Use of a TrustManager that Does Not Validate Certificate Chains

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

Sample

This sample connects to https://tlsexpired.no, which has an expired certificate, to demonstrate the insecure use of a custom TrustManager that ignores certificate chain validity. It does this by overriding the checkServerTrusted(...) method and leaving it empty, which effectively disables certificate validation.

 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
59
60
package org.owasp.mastestapp

import android.content.Context
import android.util.Log
import java.net.URL
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

class MastgTest(private val context: Context) {

    fun mastgTest(): String {
        val content = StringBuilder("Response:\n\n")
        val thread = Thread {
            content.append(fetchUrl("https://tlsexpired.no"))     // Expired cert
        }
        thread.start()
        thread.join()
        return content.toString()
    }

    private fun fetchUrl(urlString: String): String {
        return try {
            val url = URL(urlString)
            val connection = url.openConnection() as HttpsURLConnection

            trustAllCertificates(connection) // ❌ Ignores certificate chain validity

            connection.setRequestProperty("User-Agent", "OWASP MAS APP 9000")
            connection.connect()

            val response = connection.inputStream.bufferedReader().use { it.readText() }
            "\n[$urlString] Response OK\n$response\n"
        } catch (e: Exception) {
            "\n[$urlString] Error: ${e.message}\n"
        }
    }

    private fun trustAllCertificates(connection: HttpsURLConnection) {
        try {
            val trustAllCerts = arrayOf<TrustManager>(
                object : X509TrustManager {
                    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
                    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
                    override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
                }
            )

            val sslContext = SSLContext.getInstance("TLS")
            sslContext.init(null, trustAllCerts, SecureRandom())
            connection.sslSocketFactory = sslContext.socketFactory

        } catch (e: Exception) {
            Log.e("TRUST_MANAGER", "Failed to setup trust manager: ${e.message}")
        }
    }
}
  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
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
package org.owasp.mastestapp;

import android.content.Context;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import kotlin.Metadata;
import kotlin.io.CloseableKt;
import kotlin.io.TextStreamsKt;
import kotlin.jvm.internal.Intrinsics;
import kotlin.text.Charsets;

/* compiled from: MastgTest.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\u000e\n\u0002\b\u0003\n\u0002\u0010\u0002\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\u0006\u0010\u0006\u001a\u00020\u0007J\u0010\u0010\b\u001a\u00020\u00072\u0006\u0010\t\u001a\u00020\u0007H\u0002J\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/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "fetchUrl", "urlString", "trustAllCertificates", "", "connection", "Ljavax/net/ssl/HttpsURLConnection;", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
/* loaded from: classes3.dex */
public final class MastgTest {
    public static final int $stable = 8;
    private final Context context;

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

    public final String mastgTest() throws InterruptedException {
        final StringBuilder content = new StringBuilder("Response:\n\n");
        Thread thread = new Thread(new Runnable() { // from class: org.owasp.mastestapp.MastgTest$$ExternalSyntheticLambda0
            @Override // java.lang.Runnable
            public final void run() {
                MastgTest.mastgTest$lambda$0(content, this);
            }
        });
        thread.start();
        thread.join();
        String string = content.toString();
        Intrinsics.checkNotNullExpressionValue(string, "toString(...)");
        return string;
    }

    /* JADX INFO: Access modifiers changed from: private */
    public static final void mastgTest$lambda$0(StringBuilder content, MastgTest this$0) {
        Intrinsics.checkNotNullParameter(content, "$content");
        Intrinsics.checkNotNullParameter(this$0, "this$0");
        content.append(this$0.fetchUrl("https://tlsexpired.no"));
    }

    private final String fetchUrl(String urlString) throws IOException {
        try {
            URL url = new URL(urlString);
            URLConnection uRLConnectionOpenConnection = url.openConnection();
            Intrinsics.checkNotNull(uRLConnectionOpenConnection, "null cannot be cast to non-null type javax.net.ssl.HttpsURLConnection");
            HttpsURLConnection connection = (HttpsURLConnection) uRLConnectionOpenConnection;
            trustAllCertificates(connection);
            connection.setRequestProperty("User-Agent", "OWASP MAS APP 9000");
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            Intrinsics.checkNotNullExpressionValue(inputStream, "getInputStream(...)");
            Reader inputStreamReader = new InputStreamReader(inputStream, Charsets.UTF_8);
            BufferedReader bufferedReader = inputStreamReader instanceof BufferedReader ? (BufferedReader) inputStreamReader : new BufferedReader(inputStreamReader, 8192);
            try {
                BufferedReader it = bufferedReader;
                String response = TextStreamsKt.readText(it);
                CloseableKt.closeFinally(bufferedReader, null);
                return "\n[" + urlString + "] Response OK\n" + response + "\n";
            } finally {
            }
        } catch (Exception e) {
            return "\n[" + urlString + "] Error: " + e.getMessage() + "\n";
        }
    }

    private final void trustAllCertificates(HttpsURLConnection connection) throws NoSuchAlgorithmException, KeyManagementException {
        try {
            TrustManager[] trustAllCerts = {new X509TrustManager() { // from class: org.owasp.mastestapp.MastgTest$trustAllCertificates$trustAllCerts$1
                @Override // javax.net.ssl.X509TrustManager
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                }

                @Override // javax.net.ssl.X509TrustManager
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                }

                @Override // javax.net.ssl.X509TrustManager
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }};
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new SecureRandom());
            connection.setSSLSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e) {
            Log.e("TRUST_MANAGER", "Failed to setup trust manager: " + e.getMessage());
        }
    }
}

If the app wouldn't use the insecure TrustManager, you would see this message:

[https://tlsexpired.no] Error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Steps

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

../../../../rules/mastg-android-network-checkservertrusted.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
rules:
  - id: mastg-android-network-checkservertrusted
    severity: WARNING
    languages:
      - java
    metadata:
      summary: This rule looks for the use of checkServerTrusted and ensures it throws an exception instead of silently muting invalid server certificates
    message: Improper Server Certificate verification detected.
    match:
        any:
        - public void checkServerTrusted (...) { ... }
run.sh
1
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-network-checkservertrusted.yml ./MastgTest_reversed.java --text > output.txt

Observation

The rule identified one instance in the code where checkServerTrusted(...) is used without exception handling.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
┌────────────────┐
 1 Code Finding 
└────────────────┘

    MastgTest_reversed.java
    ❯❱ rules.mastg-android-network-checkservertrusted
          Improper Server Certificate verification detected.

           92 @Override // javax.net.ssl.X509TrustManager
           93 public void checkServerTrusted(X509Certificate[] chain, String authType) {
           94 }

Evaluation

The test fails because of the presence of the checkServerTrusted(...) method on in the TrustManager implementation, as well as the absence of exceptions being thrown.