Catégorie OWASP : MASVS-CODE : qualité du code
Présentation
Il n'est pas rare de voir des applications implémenter des fonctionnalités permettant aux utilisateurs de transférer des données ou d'interagir avec d'autres appareils à l'aide de communications par radiofréquence (RF) ou de connexions filaires. Les technologies les plus courantes utilisées dans Android à cette fin sont le Bluetooth classique (Bluetooth BR/EDR), le Bluetooth à faible consommation (BLE), le Wi-Fi P2P, le NFC et l'USB.
Ces technologies sont généralement implémentées dans des applications qui doivent communiquer avec des accessoires pour la maison connectée, des appareils de surveillance de la santé, des bornes de transports en commun, des terminaux de paiement et d'autres appareils Android.
Comme pour tout autre canal, les communications de machine à machine sont sensibles à d’attaques qui visent à compromettre la limite de confiance établie entre deux ou plus d'appareils. Des techniques comme l'usurpation d'identité d'appareil peuvent être aux utilisateurs malveillants d'attaquer le système de communication canal.
Android met à la disposition des développeurs des API spécifiques pour configurer les communications de machine à machine.
Ces API doivent être utilisées avec précaution, car des erreurs lors de l'implémentation de protocoles de communication peuvent entraîner l'exposition de données utilisateur ou d'appareil à des tiers non autorisés. Dans le pire des cas, les pirates informatiques pourraient être en mesure prendre le contrôle d'un ou plusieurs appareils, et obtient ainsi un accès complet au contenu. sur l'appareil.
Impact
L'impact peut varier en fonction de la technologie d'appareil à appareil mise en œuvre dans l'application.
La mauvaise utilisation ou configuration de machine à machine les canaux de communication peuvent laisser l'appareil de l'utilisateur exposé à des vos tentatives de communication. Cela peut rendre l'appareil vulnérable à attaques supplémentaires de type man-in-the-middle (MiTM), injections de commandes, DoS, les attaques par usurpation d'identité.
Risque: écoute clandestine de données sensibles sur les canaux sans fil
Lors de l'implémentation de mécanismes de communication de machine à machine, vous devez tenir compte à la fois de la technologie utilisée et du type de données à transmettre. Bien que les connexions filaires soient en pratique plus sécurisées pour de telles tâches, car elles nécessitent un lien physique entre les appareils concernés, les protocoles de communication utilisant des fréquences radio telles que le Bluetooth classique, le BLE, le NFC et le Wi-Fi P2P peuvent être interceptés. Un pirate informatique peut être en mesure d'usurper l'identité de l'un des terminaux ou des points d'accès impliqués dans l'échange de données, d'intercepter la communication sans fil et, par conséquent, d'accéder aux données utilisateur sensibles. De plus, si des applications malveillantes installées sur l'appareil sont autorisées à utiliser les autorisations d'exécution spécifiques à la communication, elles peuvent être en mesure de récupérer les données échangées entre les appareils en lisant les tampons de messages système.
Stratégies d'atténuation
Si l'application nécessite un échange de données sensibles entre plusieurs machines via des canaux sans fil, les solutions de sécurité au niveau de la couche d'application, comme doit être implémentée dans le code de l'application. Cela empêche les pirates informatiques d'espionner le canal de communication et de récupérer les données échangées en texte brut. Pour accéder à d'autres ressources, reportez-vous au Cryptographie.
Risque : injection de données malveillantes sans fil
Canaux de communication sans fil entre machines (Bluetooth classique, BLE, NFC, (Wi-Fi P2P) peuvent être altérés à l'aide de données malveillantes. Des pirates informatiques suffisamment qualifiés peuvent identifier le protocole de communication utilisé et falsifier le flux d'échange de données, par exemple en usurpant l'identité de l'un des points de terminaison et en envoyant des charges utiles spécialement conçues. Ce type de trafic malveillant peut dégrader fonctionnalité d'une application et, dans le pire des cas, provoquer des erreurs le comportement des applications et des appareils, ou entraîner des attaques de type DoS, une injection ou une reprise d'appareil.
Stratégies d'atténuation
Android fournit aux développeurs des API puissantes pour gérer les communications de machine à machine telles que le Bluetooth classique, le BLE, le NFC et le Wi-Fi P2P. Ils doivent être associés à une logique de validation des données soigneusement implémentée. pour nettoyer toutes les données échangées entre deux appareils.
Cette solution doit être implémentée au niveau de l'application et doit inclure vérifie que les données ont la longueur et le format attendus, et contiennent un une charge utile valide pouvant être interprétée par l'application.
L'extrait de code suivant présente un exemple de logique de validation des données. Cela a été implémenté sur l'exemple des développeurs Android pour implémenter le transfert de données Bluetooth :
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;
}
}
Risque : injection de données malveillantes via un USB
Les connexions USB entre deux appareils peuvent être ciblées par un utilisateur malveillant qui souhaite intercepter les communications. Dans ce cas, le lien physique requis constitue une couche de sécurité supplémentaire, car le pirate informatique doit accéder au câble qui relie les terminaux pour pouvoir intercepter tout message. Un autre vecteur d'attaque est représenté par des périphériques USB non fiables qui, que ce soit de manière intentionnelle ou non, sont branchés à l'appareil.
Si l'application filtre les appareils USB à l'aide de PID/VID pour déclencher des fonctionnalités spécifiques dans l'application, les pirates informatiques peuvent falsifier les données envoyées via le canal USB en usurpant l'identité de l'appareil légitime. De telles attaques peuvent permettre à des utilisateurs malveillants d'envoyer des frappes à l'appareil ou d'exécuter des activités d'application qui, dans le pire des cas, peuvent entraîner l'exécution de code à distance ou le téléchargement de logiciels indésirables.
Stratégies d'atténuation
Une logique de validation au niveau de l'application doit être implémentée. Cette logique doit filtrer les données envoyées via USB en vérifiant que la longueur, le format et le contenu correspondant au cas d'utilisation de l'application. Par exemple, un cardiofréquencemètre ne devrait pas être en mesure d'envoyer des commandes de frappe.
En outre, lorsque cela est possible, il est recommandé de limiter le nombre de paquets USB que l'application peut recevoir de l'appareil USB. Cela empêche les appareils malveillants d'effectuer des attaques telles que le canard en caoutchou.
Cette validation peut être effectuée en créant un nouveau thread pour vérifier
du contenu mis en mémoire tampon, par exemple sur 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();
}
}
Risques spécifiques
Cette section présente les risques qui nécessitent des stratégies d'atténuation non standards ou qui ont été atténués à certains niveaux du SDK et qui sont ici complets.
Risque : Bluetooth : durée de visibilité incorrecte
Comme indiqué dans la documentation sur le Bluetooth destinée aux développeurs Android,
configuration de l'interface Bluetooth dans l'application, en utilisant
Méthode startActivityForResult(Intent, int)
pour activer l'appareil
la visibilité et la définition de EXTRA_DISCOVERABLE_DURATION
sur zéro
peut rendre l'appareil visible tant que l'application est en cours d'exécution
en arrière-plan ou au premier plan. Comme pour la spécification Bluetooth classique, les appareils détectables diffusent en permanence des informations spécifiques
messages permettant à d'autres appareils de récupérer leurs données ou de s'y connecter. Dans
dans ce scénario, un tiers malveillant peut intercepter ces messages et se connecter
à l'appareil Android. Une fois connecté, un pirate informatique peut effectuer d'autres attaques, telles que le vol de données, les attaques DoS ou l'injection de commandes.
Stratégies d'atténuation
EXTRA_DISCOVERABLE_DURATION
ne doit jamais être défini sur zéro. Si le paramètre EXTRA_DISCOVERABLE_DURATION
n'est pas défini, Android rend les appareils détectables pendant deux minutes par défaut. La valeur maximale pouvant être définie pour
Le paramètre EXTRA_DISCOVERABLE_DURATION
est défini sur 2 heures (7 200 secondes). Il est
recommandé pour que la durée de visibilité soit la plus courte possible
possible, en fonction du cas d'utilisation de l'application.
Risque: NFC (filtres d'intent clonés)
Une application malveillante peut enregistrer des filtres d'intent pour lire des tags NFC ou des appareils compatibles NFC spécifiques. Ces filtres peuvent reproduire ceux définis par une règle application légitime, permettant à un attaquant de lire le contenu des données NFC échangées. Notez que lorsque deux activités spécifient les mêmes filtres d'intent pour une balise NFC spécifique, le sélecteur d'activités est présenté. L'utilisateur doit donc toujours choisir l'application malveillante pour que l'attaque réussisse. Néanmoins, la combinaison des filtres d'intent et des techniques de dissimulation, ce scénario est toujours possible. Cette attaque est n'est significatif que si les données échangées via la technologie NFC peuvent être considérées très sensibles.
Stratégies d'atténuation
Lors de l'implémentation de fonctionnalités de lecture NFC dans une application, les filtres d'intent peuvent être utilisés avec les enregistrements d'applications Android (AAR). L'intégration de l'enregistrement AAR dans un message NDEF garantit que seule l'application légitime et son activité de gestion NDEF associée sont lancées. Cela empêchera les activités ou applications indésirables de lire un contenu très des données sensibles sur l'appareil ou le traceur échangées via NFC.
Risque: NFC – absence de validation des messages NDEF
Lorsqu'un appareil Android reçoit des données provenant d'un tag NFC ou d'un appareil compatible NFC le système déclenche automatiquement l'application ou l'action spécifique d'activité configurée pour gérer le message NDEF qu'il contient. Selon la logique mise en œuvre dans l'application, les données contenues dans le ou reçues de l'appareil, peuvent être envoyées à d'autres activités pour déclencher d'autres actions, telles que l'ouverture de pages Web.
Une application sans validation du contenu des messages NDEF peut permettre à des pirates informatiques utiliser des appareils compatibles NFC ou des tags NFC pour injecter des charges utiles malveillantes à l'intérieur du application, provoquant un comportement inattendu pouvant entraîner la création téléchargement, injection de commandes ou DoS.
Stratégies d'atténuation
Avant de distribuer le message NDEF reçu à tout autre composant d'application, les données qu'il contient doivent être validées pour être au format attendu et contenir les les informations attendues. Cela évite que des données malveillantes ne soient transmises à d'autres des applications les composants non filtrés, ce qui réduit le risque de comportement inattendu ou à l'aide de données NFC falsifiées.
L'extrait de code suivant montre un exemple de logique de validation des données implémentée en tant que avec un message NDEF en tant qu'argument et son index dans le tableau de messages. Ce code a été implémenté à l'aide de l'exemple de développeurs Android afin d'obtenir des données à partir d'un tag NDEF NFC scanné:
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;
}
Ressources
- Autorisations d'exécution
- Guides de connectivité
- Exemple
- transfert groupé
- Cryptographie
- Configurer le Bluetooth
- NFC Basis
- Enregistrements d'applications Android
- Caractéristiques Bluetooth classiques