Skip to content

MASTG-DEMO-0009: Detecção de Dados Sensíveis no Network Traffic

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

Amostra

O trecho abaixo mostra um código de exemplo que envia dados sensíveis pela rede usando a classe HttpURLConnection. Os dados são enviados para https://httpbin.org/post, que é um endpoint fictício que retorna os dados recebidos.

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

import android.content.Context
import android.util.Log
import java.io.BufferedOutputStream
import java.io.BufferedWriter
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import java.util.logging.Logger

class MastgTest (private val context: Context){

    fun mastgTest(): String { 

        val SENSITIVE_DATA = mapOf(
            "precise_location_latitude" to "37.7749",
            "precise_location_longitude" to "-122.4194",
            "name" to "John Doe",
            "email_address" to "john.doe@example.com",
            "phone_number" to "+11234567890",
            "credit_card_number" to "1234 5678 9012 3456"
        )

        var result = ""

        val thread = Thread {
            try {
                val url = URL("https://httpbin.org/post")
                val httpURLConnection = url.openConnection() as HttpURLConnection
                httpURLConnection.requestMethod = "POST"
                httpURLConnection.doOutput = true
                httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")

                // Creating POST data from the SENSITIVE_DATA map
                val postData = SENSITIVE_DATA.map { (key, value) ->
                    "${URLEncoder.encode(key, "UTF-8")}=${URLEncoder.encode(value, "UTF-8")}"
                }.joinToString("&")

                val outputStream = BufferedOutputStream(httpURLConnection.outputStream)
                val bufferedWriter = BufferedWriter(OutputStreamWriter(outputStream, "UTF-8"))
                bufferedWriter.write(postData)
                bufferedWriter.flush()
                bufferedWriter.close()
                outputStream.close()

                val responseCode = httpURLConnection.responseCode
                val responseContent = httpURLConnection.inputStream.bufferedReader().readText()
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    Log.d("HTTP_SUCCESS", "Successfully authenticated.")
                } else {
                    Log.e("HTTP_ERROR", "Failed to authenticate. Response code: $responseCode")
                }
                result = "$responseCode\n\n$responseContent"

            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        thread.start()
        thread.join()

        return result
    }

}
 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
package org.owasp.mastestapp;

import android.content.Context;
import android.util.Log;
import androidx.autofill.HintConstants;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import kotlin.Metadata;
import kotlin.TuplesKt;
import kotlin.collections.CollectionsKt;
import kotlin.collections.MapsKt;
import kotlin.jvm.internal.Intrinsics;
import kotlin.jvm.internal.Ref;

/* compiled from: MastgTest.kt */
@Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\u0006\u0010\u0005\u001a\u00020\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0007"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "(Landroid/content/Context;)V", "mastgTest", "", "app_debug"}, k = 1, mv = {1, 9, 0}, xi = 48)
/* loaded from: classes4.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;
    }

    /* JADX WARN: Multi-variable type inference failed */
    public final String mastgTest() {
        final Map SENSITIVE_DATA = MapsKt.mapOf(TuplesKt.to("precise_location_latitude", "37.7749"), TuplesKt.to("precise_location_longitude", "-122.4194"), TuplesKt.to(HintConstants.AUTOFILL_HINT_NAME, "John Doe"), TuplesKt.to("email_address", "john.doe@example.com"), TuplesKt.to("phone_number", "+11234567890"), TuplesKt.to("credit_card_number", "1234 5678 9012 3456"));
        final Ref.ObjectRef result = new Ref.ObjectRef();
        result.element = "";
        Thread thread = new Thread(new Runnable() { // from class: org.owasp.mastestapp.MastgTest$$ExternalSyntheticLambda0
            @Override // java.lang.Runnable
            public final void run() {
                MastgTest.mastgTest$lambda$1(SENSITIVE_DATA, result);
            }
        });
        thread.start();
        thread.join();
        return (String) result.element;
    }

    /* JADX INFO: Access modifiers changed from: private */
    /* JADX WARN: Type inference failed for: r7v10, types: [T, java.lang.String] */
    public static final void mastgTest$lambda$1(Map SENSITIVE_DATA, Ref.ObjectRef result) {
        Intrinsics.checkNotNullParameter(SENSITIVE_DATA, "$SENSITIVE_DATA");
        Intrinsics.checkNotNullParameter(result, "$result");
        try {
            URL url = new URL("https://httpbin.org/post");
            URLConnection openConnection = url.openConnection();
            Intrinsics.checkNotNull(openConnection, "null cannot be cast to non-null type java.net.HttpURLConnection");
            HttpURLConnection httpURLConnection = (HttpURLConnection) openConnection;
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            Collection destination$iv$iv = new ArrayList(SENSITIVE_DATA.size());
            for (Map.Entry item$iv$iv : SENSITIVE_DATA.entrySet()) {
                String key = (String) item$iv$iv.getKey();
                String value = (String) item$iv$iv.getValue();
                destination$iv$iv.add(URLEncoder.encode(key, "UTF-8") + '=' + URLEncoder.encode(value, "UTF-8"));
                url = url;
            }
            String postData = CollectionsKt.joinToString$default((List) destination$iv$iv, "&", null, null, 0, null, null, 62, null);
            BufferedOutputStream outputStream = new BufferedOutputStream(httpURLConnection.getOutputStream());
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
            bufferedWriter.write(postData);
            bufferedWriter.flush();
            bufferedWriter.close();
            outputStream.close();
            int responseCode = httpURLConnection.getResponseCode();
            if (responseCode == 200) {
                Log.d("HTTP_SUCCESS", "Successfully authenticated.");
            } else {
                Log.e("HTTP_ERROR", "Failed to authenticate. Response code: " + responseCode);
            }
            result.element = responseCode + "\n\n" + postData;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Passos

Inicie o dispositivo, neste caso, o emulador Android:

emulator -avd Pixel_3a_API_33_arm64-v8a -writable-system

Execute mitmproxy com o script personalizado para registrar dados sensíveis e despeje o tráfego relevante em um arquivo.

Observe que o script está pré-configurado com dados já considerados sensíveis para este aplicativo. Ao executar este teste em um cenário real, você deve determinar o que é considerado dados sensíveis) com base na política de privacidade do aplicativo e nas regulamentações de privacidade relevantes. Uma maneira recomendada de fazer isso é verificando a política de privacidade do aplicativo e as declarações de privacidade da App Store.

mitm_sensitive_logger.py
 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
from mitmproxy import http

# This data would come from another file and should be defined after identifying the data that is considered sensitive for this application.
# For example by using the Google Play Store Data Safety section.
SENSITIVE_DATA = {
    "precise_location_latitude": "37.7749",
    "precise_location_longitude": "-122.4194",
    "name": "John Doe",
    "email_address": "john.doe@example.com",
    "phone_number": "+11234567890",
    "credit_card_number": "1234 5678 9012 3456"
}

SENSITIVE_STRINGS = SENSITIVE_DATA.values()

def contains_sensitive_data(string):
    return any(sensitive in string for sensitive in SENSITIVE_STRINGS)

def process_flow(flow):
    url = flow.request.pretty_url
    request_headers = flow.request.headers
    request_body = flow.request.text
    response_headers = flow.response.headers if flow.response else "No response"
    response_body = flow.response.text if flow.response else "No response"

    if (contains_sensitive_data(url) or 
        contains_sensitive_data(request_body) or 
        contains_sensitive_data(response_body)):
        with open("sensitive_data.log", "a") as file:
            if flow.response:
                file.write(f"RESPONSE URL: {url}\n")
                file.write(f"Response Headers: {response_headers}\n")
                file.write(f"Response Body: {response_body}\n\n")
            else:
                file.write(f"REQUEST URL: {url}\n")
                file.write(f"Request Headers: {request_headers}\n")
                file.write(f"Request Body: {request_body}\n\n")
def request(flow: http.HTTPFlow):
    process_flow(flow)

def response(flow: http.HTTPFlow):
    process_flow(flow)
run.sh
1
mitmdump -s mitm_sensitive_logger.py

Inicie o aplicativo a partir do Android Studio e clique no botão que enviará os dados sensíveis pela rede. O script capturará o tráfego de rede e registrará os dados sensíveis.

Observação

O script identificou várias instâncias de dados sensíveis no tráfego de rede.

  • A primeira instância é uma requisição POST para https://httpbin.org/post que contém os valores de dados sensíveis no corpo da requisição.
  • A segunda instância é uma resposta de https://httpbin.org/post que contém os valores de dados sensíveis no corpo da resposta.
sensitive_data.log
 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
REQUEST URL: https://httpbin.org/post
Request Headers: Headers[(b'Content-Type', b'application/x-www-form-urlencoded'), (b'User-Agent', b'Dalvik/2.1.0 (Linux; U; Android 13; sdk_gphone64_arm64 Build/TE1A.220922.021)'), (b'Host', b'httpbin.org'), (b'Connection', b'Keep-Alive'), (b'Accept-Encoding', b'gzip'), (b'Content-Length', b'188')]
Request Body: precise_location_latitude=37.7749&precise_location_longitude=-122.4194&name=John+Doe&email_address=john.doe%40example.com&phone_number=%2B11234567890&credit_card_number=1234+5678+9012+3456

RESPONSE URL: https://httpbin.org/post
Response Headers: Headers[(b'Date', b'Fri, 19 Jan 2024 10:17:44 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'735'), (b'Connection', b'keep-alive'), (b'Server', b'gunicorn/19.9.0'), (b'Access-Control-Allow-Origin', b'*'), (b'Access-Control-Allow-Credentials', b'true')]
Response Body: {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "credit_card_number": "1234 5678 9012 3456", 
    "email_address": "john.doe@example.com", 
    "name": "John Doe", 
    "phone_number": "+11234567890", 
    "precise_location_latitude": "37.7749", 
    "precise_location_longitude": "-122.4194"
  }, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "188", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 13; sdk_gphone64_arm64 Build/TE1A.220922.021)", 
    "X-Amzn-Trace-Id": "Root=1-65aa4c48-45514c0e3782665063b14397"
  }, 
  "json": null, 
  "origin": "148.141.65.87", 
  "url": "https://httpbin.org/post"
}

Avaliação

Após revisar o tráfego de rede capturado, podemos concluir que o teste falha porque os dados sensíveis são enviados pela rede.

Este é um exemplo fictício, mas em um cenário real, você deve determinar quais das instâncias relatadas são relevantes para a privacidade e precisam ser tratadas.

Observe que tanto a requisição quanto a resposta estão criptografadas usando TLS, portanto, podem ser consideradas seguras. No entanto, isso pode representar um problema de privacidade dependendo das regulamentações de privacidade relevantes e da política de privacidade do aplicativo. Agora você deve verificar a política de privacidade e as declarações de privacidade da App Store para ver se o aplicativo tem permissão para enviar esses dados para terceiros.