Configurazione della comunicazione tra macchine non sicura

Categoria OWASP: MASVS-CODE: Qualità del codice

Panoramica

Non è raro vedere applicazioni che implementano funzionalità che consentono agli utenti di Trasferire dati o interagire con altri dispositivi utilizzando le radiofrequenze (RF) comunicazioni o connessioni cablate. Le tecnologie più comuni utilizzate in I dispositivi Android a questo scopo sono Bluetooth classico (Bluetooth BR/EDR), Bluetooth Low Energia (BLE), Wi-Fi P2P, NFC e USB.

Queste tecnologie vengono in genere implementate in applicazioni che dovrebbero comunicare con accessori per la smart home, dispositivi di monitoraggio della salute, chioschi per il trasporto pubblico, terminali di pagamento e altri dispositivi basati su Android.

Come per qualsiasi altro canale, le comunicazioni machine-to-machine sono soggette a attacchi volti a compromettere il confine di attendibilità stabilito tra due o più dispositivi. Tecniche come la rappresentazione di un dispositivo possono essere sfruttate da utenti malintenzionati per eseguire un numero elevato di attacchi contro il canale di comunicazione.

Android mette a disposizione degli sviluppatori API specifiche per la configurazione delle comunicazioni macchina-a-macchina.

Queste API devono essere utilizzate con cautela come errori durante l'implementazione della comunicazione dei protocolli può comportare l'esposizione dei dati dell'utente o del dispositivo a e terze parti. Nel peggiore dei casi, gli aggressori potrebbero riuscire a eseguire operazioni assumere il controllo di uno o più dispositivi, ottenendo di conseguenza l'accesso completo ai contenuti sul dispositivo.

Impatto

L'impatto può variare in base alla tecnologia da dispositivo a dispositivo implementata in per l'applicazione.

Utilizzo o configurazione non corretto del processo da macchina a macchina canali di comunicazione potrebbero lasciare il dispositivo dell'utente esposto a non attendibili tentativi di comunicazione. Questo può rendere il dispositivo vulnerabile a come attacchi man in the middle (MiTM), command injection, DoS o attacchi di impersonificazione.

Rischio: intercettazione di dati sensibili tramite canali wireless

Quando implementi meccanismi di comunicazione tra macchine, è necessario considerare sia la tecnologia utilizzata sia il tipo di dati che devono essere trasmessi. Sebbene le connessioni via cavo siano in pratica più sicure per queste attività, in quanto richiedono un collegamento fisico tra i dispositivi coinvolti, i protocolli di comunicazione che utilizzano frequenze radio come il Bluetooth classico, BLE, NFC e il Wi-Fi P2P possono essere intercettati. Un malintenzionato potrebbe essere in grado di rubare l'identità di uno dei terminali o dei punti di accesso coinvolti nello scambio di dati, intercettando la comunicazione via etere e ottenendo così l'accesso a dati sensibili degli utenti. Inoltre, le applicazioni dannose installate sul dispositivo, se concesse le autorizzazioni di runtime specifiche della comunicazione, potrebbero essere in grado di recuperare i dati scambiati tra i dispositivi leggendo i buffer dei messaggi di sistema.

Mitigazioni

Se l'applicazione richiede lo scambio di dati sensibili tra macchine su canali wireless, le soluzioni di sicurezza a livello di applicazione, come la crittografia, devono essere implementate nel codice dell'applicazione. In questo modo, gli utenti malintenzionati non potranno eseguire attività di sniffing sul canale di comunicazione e recuperare i dati scambiati in testo non criptato. Per risorse aggiuntive, consulta Crittografia.


Rischio: inserimento di dati dannosi tramite rete wireless

Canali di comunicazione wireless tra macchina (Bluetooth classico, BLE, NFC, Wifi P2P) possono essere manomessi usando dati dannosi. Competenza sufficiente i malintenzionati possono identificare il protocollo di comunicazione in uso e manomettere flusso di scambio di dati, ad esempio simulando l'identità di uno degli endpoint, appositamente creati. Questo tipo di traffico dannoso potrebbe peggiorare la funzionalità dell'applicazione e, nel peggiore dei casi, causare comportamenti imprevisti dell'applicazione e del dispositivo o attacchi come DoS, attacco di comando o compromissione del dispositivo.

Mitigazioni

Android fornisce agli sviluppatori API potenti per gestire comunicazioni tra macchina, come Bluetooth classico, BLE, NFC e Wi-Fi P2P. Questi dovrebbero essere combinati con una logica di convalida dei dati accuratamente implementata per sanificare i dati scambiati tra i due dispositivi.

Questa soluzione deve essere implementata a livello di applicazione e deve includere controlli che verificano se i dati hanno la lunghezza e il formato previsti e se contengono un payload valido che può essere interpretato dall'applicazione.

Lo snippet seguente mostra un esempio di logica di convalida dei dati. Questa funzionalità è stata implementata rispetto all'esempio di implementazione dei dati Bluetooth per gli sviluppatori Android trasferimento:

Kotlin

class MyThread(private val mmInStream: InputStream, private val handler: Handler) : Thread() {

    private val mmBuffer = ByteArray(1024)
      override fun run() {
        while (true) {
            try {
                val numBytes = mmInStream.read(mmBuffer)
                if (numBytes > 0) {
                    val data = mmBuffer.copyOf(numBytes)
                    if (isValidBinaryData(data)) {
                        val readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1, data
                        )
                        readMsg.sendToTarget()
                    } else {
                        Log.w(TAG, "Invalid data received: $data")
                    }
                }
            } catch (e: IOException) {
                Log.d(TAG, "Input stream was disconnected", e)
                break
            }
        }
    }

    private fun isValidBinaryData(data: ByteArray): Boolean {
        if (// Implement data validation rules here) {
            return false
        } else {
            // Data is in the expected format
            return true
        }
    }
}

Java

public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()
            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    if (numBytes > 0) {
                        // Handle raw data directly
                        byte[] data = Arrays.copyOf(mmBuffer, numBytes);
                        // Validate the data before sending it to the UI activity
                        if (isValidBinaryData(data)) {
                            // Data is valid, send it to the UI activity
                            Message readMsg = handler.obtainMessage(
                                    MessageConstants.MESSAGE_READ, numBytes, -1,
                                    data);
                            readMsg.sendToTarget();
                        } else {
                            // Data is invalid
                            Log.w(TAG, "Invalid data received: " + data);
                        }
                    }
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        private boolean isValidBinaryData(byte[] data) {
            if (// Implement data validation rules here) {
                return false;
            } else {
                // Data is in the expected format
                return true;
           }
    }

Rischio: inserimento di dati dannosi tramite USB

Le connessioni USB tra due dispositivi potrebbero essere prese di mira da un utente malintenzionato interessati a intercettare le comunicazioni. In questo caso, il collegamento fisico richiesto costituisce un ulteriore livello di sicurezza, in quanto l'aggressore deve ottenere l'accesso al cavo che collega i terminali per poter intercettare qualsiasi messaggio. Un altro vettore di attacco è rappresentato da dispositivi USB non attendibili che, intenzionalmente o meno, sono collegati al dispositivo.

Se l'applicazione filtra i dispositivi USB utilizzando PID/VID per l'attivazione di specifiche funzionalità in-app, i malintenzionati potrebbero essere in grado di manomettere i dati inviati canale USB simulando l'identità del dispositivo legittimo. Attacchi di questo tipo possono Consentire a utenti malintenzionati di inviare sequenze di tasti al dispositivo o di eseguire applicazioni attività che, nel peggiore dei casi, possono portare all'esecuzione di codice in remoto download di software indesiderato.

Mitigazioni

Deve essere implementata una logica di convalida a livello di applicazione. Questa logica dovrebbe filtrare i dati inviati tramite USB controllando che la lunghezza, il formato e i contenuti corrispondano al caso d'uso dell'applicazione. Ad esempio, un monitor per il battito cardiaco non deve essere in grado di inviare comandi di battitura.

Inoltre, se possibile, occorre considerare la limitazione di pacchetti USB che l'applicazione può ricevere dal dispositivo USB. Questo impedisce ai dispositivi dannosi di eseguire attacchi come la paperella di gomma.

Questa convalida può essere eseguita creando un nuovo thread per controllare i contenuti del buffer, ad esempio su un bulkTransfer:

Kotlin

fun performBulkTransfer() {
    // Stores data received from a device to the host in a buffer
    val bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.size, 5000)

    if (bytesTransferred > 0) {
        if (//Checks against buffer content) {
            processValidData(buffer)
        } else {
            handleInvalidData()
        }
    } else {
        handleTransferError()
    }
}

Java

public void performBulkTransfer() {
        //Stores data received from a device to the host in a buffer
        int bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.length, 5000);
        if (bytesTransferred > 0) {
            if (//Checks against buffer content) {
                processValidData(buffer);
            } else {
                handleInvalidData();
            }
        } else {
            handleTransferError();
        }
    }

Rischi specifici

Questa sezione raccoglie i rischi che richiedono strategie di mitigazione non standard o che sono stati mitigati a un determinato livello di SDK e sono riportati per completezza.

Rischio: Bluetooth - tempo di rilevabilità errato

Come evidenziato nella documentazione relativa al Bluetooth per gli sviluppatori Android, mentre configurando l'interfaccia Bluetooth all'interno dell'applicazione, utilizzando Metodo startActivityForResult(Intent, int) per attivare il dispositivo la rilevabilità e l'impostazione di EXTRA_DISCOVERABLE_DURATION su zero il dispositivo sarà rilevabile fintanto che l'applicazione è in esecuzione in primo piano o sullo sfondo. Come per le specifiche del Bluetooth classico, i dispositivi rilevabili trasmettono costantemente informazioni specifiche messaggi che consentono ad altri dispositivi di recuperare i dati del dispositivo o di connettersi al dispositivo. Nel in uno scenario di questo tipo, una terza parte dannosa può intercettare questi messaggi e connettersi sul dispositivo Android. Una volta connesso, un malintenzionato può eseguire ulteriori attacchi come furto di dati, DoS o attacco di iniezione di comandi.

Mitigazioni

EXTRA_DISCOVERABLE_DURATION non deve mai essere impostato su zero. Se il parametro EXTRA_DISCOVERABLE_DURATION non è impostato, per impostazione predefinita Android rende i dispositivi rilevabili per 2 minuti. Il valore massimo che può essere impostato per l'attributo Il parametro EXTRA_DISCOVERABLE_DURATION dura 2 ore (7200 secondi). È consigliato per limitare il tempo di rilevabilità al tempo più breve possibili, in base al caso d'uso dell'applicazione.


Rischio: NFC - filtri intent clonati

Un'applicazione dannosa può registrare filtri intent per leggere tag NFC o dispositivi con tecnologia NFC specifici. Questi filtri possono replicare quelli definiti da un'applicazione legittima, consentendo a un malintenzionato di leggere i contenuti dei dati NFC scambiati. È opportuno notare che, quando due attività specificano gli stessi filtri intent per un tag NFC specifico, viene visualizzato il Selettore di attività, pertanto l'utente dovrà comunque scegliere l'applicazione dannosa affinché l'attacco vada a buon fine. Tuttavia, combinando filtri per intent con il cloaking, questo scenario è ancora possibile. Questo attacco è significativo solo nei casi in cui i dati scambiati tramite NFC possono essere considerati molto sensibile.

Mitigazioni

Quando implementi funzionalità di lettura NFC all'interno di un'applicazione, i filtri intent possono essere utilizzati insieme ai record delle applicazioni Android (AAR). Incorporando il Il record AAR all'interno di un messaggio NDEF dà la forte garanzia che solo il legittima e l'attività di gestione NDEF associata, viene avviata. In questo modo, le applicazioni o le attività indesiderate non potranno leggere i dati dei tag o dei dispositivi altamente sensibili scambiati tramite NFC.


Rischio: NFC - Mancanza di convalida dei messaggi NDEF

Quando un dispositivo Android riceve dati da un tag NFC o da un dispositivo con NFC abilitato, il sistema attiva automaticamente l'applicazione o l'attività specifica configurata per gestire il messaggio NDEF al suo interno. In base alla logica implementata nell'applicazione, i dati contenuti nel tag o ricevuti dal dispositivo possono essere inviati ad altre attività per attivare ulteriori azioni, come l'apertura di pagine web.

Un’applicazione priva della convalida dei contenuti dei messaggi NDEF potrebbe consentire agli utenti malintenzionati di utilizzano dispositivi abilitati per NFC o tag NFC per iniettare payload dannosi all’interno causando un comportamento imprevisto che potrebbe causare file dannosi download, command injection o DoS.

Mitigazioni

Prima di inviare il messaggio NDEF ricevuto a qualsiasi altro componente dell'applicazione, i dati al suo interno devono essere convalidati per verificare che siano nel formato previsto e che contengano le informazioni previste. In questo modo eviterai che i dati dannosi vengano trasmessi ad altri delle applicazioni componenti non filtrati, riducendo il rischio di comportamenti imprevisti attacchi che utilizzano dati NFC manomessi.

Lo snippet seguente mostra un esempio di logica di convalida dei dati implementata come metodo con un messaggio NDEF come argomento e il relativo indice nell'array di messaggi. Questa funzionalità è stata implementata nell'esempio degli sviluppatori Android per ottenere dati da una tag NDEF NFC scansionato:

Kotlin

//The method takes as input an element from the received NDEF messages array
fun isValidNDEFMessage(messages: Array<NdefMessage>, index: Int): Boolean {
    // Checks if the index is out of bounds
    if (index < 0 || index >= messages.size) {
        return false
    }
    val ndefMessage = messages[index]
    // Retrieves the record from the NDEF message
    for (record in ndefMessage.records) {
        // Checks if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
        if (record.tnf == NdefRecord.TNF_ABSOLUTE_URI && record.type.size == 1) {
            // Loads payload in a byte array
            val payload = record.payload

            // Declares the Magic Number that should be matched inside the payload
            val gifMagicNumber = byteArrayOf(0x47, 0x49, 0x46, 0x38, 0x39, 0x61) // GIF89a

            // Checks the Payload for the Magic Number
            for (i in gifMagicNumber.indices) {
                if (payload[i] != gifMagicNumber[i]) {
                    return false
                }
            }
            // Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
            if (payload.size == 13) {
                return true
            }
        }
    }
    return false
}

Java

//The method takes as input an element from the received NDEF messages array
    public boolean isValidNDEFMessage(NdefMessage[] messages, int index) {
        //Checks if the index is out of bounds
        if (index < 0 || index >= messages.length) {
            return false;
        }
        NdefMessage ndefMessage = messages[index];
        //Retrieve the record from the NDEF message
        for (NdefRecord record : ndefMessage.getRecords()) {
            //Check if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
            if ((record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) && (record.getType().length == 1)) {
                //Loads payload in a byte array
                byte[] payload = record.getPayload();
                //Declares the Magic Number that should be matched inside the payload
                byte[] gifMagicNumber = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; // GIF89a
                //Checks the Payload for the Magic Number
                for (int i = 0; i < gifMagicNumber.length; i++) {
                    if (payload[i] != gifMagicNumber[i]) {
                        return false;
                    }
                }
                //Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
                if (payload.length == 13) {
                    return true;
                }
            }
        }
        return false;
    }

Risorse