Kategoria OWASP: MASVS-CODE: jakość kodu
Omówienie
Nierzadko zdarzają się aplikacje, które implementują funkcje umożliwiające użytkownikom przesyłanie danych lub interakcję z innymi urządzeniami za pomocą komunikacji radiowej (RF) lub połączeń przewodowych. Najczęściej używane w Androidzie technologie to klasyczny Bluetooth (Bluetooth BR/EDR), Bluetooth Low Energy (BLE), Wi-Fi P2P, NFC i USB.
Technologie te są zwykle wdrażane w aplikacji, która ma komunikować się z akcesoriami do inteligentnego domu, urządzeniami do monitorowania zdrowia, kioskami do obsługi transportu publicznego, terminalami płatniczymi i innymi urządzeniami z Androidem.
Tak jak w przypadku każdego innego kanału, komunikacja między maszynami jest podatna na ataki mające na celu pokonanie granicy zaufania ustanowionej między dwoma lub więcej urządzeń. Złośliwi użytkownicy mogą wykorzystywać techniki takie jak podszywanie się pod inne urządzenie, aby przeprowadzać liczne ataki na kanał komunikacji.
Android udostępnia deweloperom konkretne interfejsy API do konfigurowania komunikacji maszyna–maszyna.
Z tych interfejsów API należy korzystać z rozwagą, ponieważ przy wdrażaniu komunikacji pojawiają się błędy mogą narazić dane użytkownika lub urządzenia na nieupoważnione osób trzecich. W najgorszym przypadku atakujący mogą przejąć kontrolę nad jednym lub wieloma urządzeniami, uzyskując w ten sposób pełny dostęp do zawartości na urządzeniu.
Wpływ
Wpływ może się różnić w zależności od technologii komunikacji między urządzeniami zaimplementowanej w aplikacji.
Niewłaściwe użycie lub konfiguracja między maszynami kanały komunikacji mogą narazić urządzenie użytkownika na niezaufane prób komunikacji. Może to sprawić, że urządzenie jest podatne na dodatkowe ataki, takie jak „man in the middle” (MiTM), wstrzykiwanie dowodów, DoS (atak typu DoS) podszywania się pod inne osoby.
Zagrożenie: podsłuchiwanie danych wrażliwych przez kanały bezprzewodowe
Podczas wdrażania mechanizmów komunikacji maszyna–maszyna należy dokładnie rozważyć zarówno używaną technologię, jak i typ danych, które mają być przesyłane. Chociaż połączenia kablowe są w praktyce bezpieczniejsze ponieważ wymagają fizycznego połączenia między urządzeniami. protokoły komunikacyjne wykorzystujące częstotliwości radiowe, takie jak klasyczny Bluetooth, BLE, NFC i sieć Wi-Fi P2P mogą być przechwytywane. Osoba przeprowadzająca atak może podszyć się pod inną osobę terminali lub punktów dostępu uczestniczących w wymianie danych, przechwytując komunikacji bezprzewodowej, co pozwala uzyskać dostęp do wrażliwych użytkowników i skalowalnych danych. Dodatkowo złośliwe aplikacje zainstalowane na urządzeniu (jeśli przyznano te uprawnienia). związane z komunikacją uprawnienia czasu działania mogą pobrać danych wymienianych między urządzeniami przez odczytywanie buforów komunikatów systemowych.
Środki zaradcze
czy aplikacja wymaga wymiany danych wrażliwych między maszynami. przez kanały bezprzewodowe, a także rozwiązania zabezpieczające w warstwie aplikacji, takie jak szyfrowania, należy zaimplementować w kodzie aplikacji. Dzięki temu zapobiegnie przed przechwytywaniem kanału komunikacyjnego i odzyskaniem że dane wymieniane są jawnym tekstem. Więcej informacji znajdziesz tutaj: Dokumentacja kryptografii.
Ryzyko: wstrzykiwanie złośliwych danych przez sieć bezprzewodową
Bezprzewodowe kanały komunikacji maszyna-maszyna (klasyczny Bluetooth, BLE, NFC, Wi-Fi P2P) mogą zostać naruszone za pomocą złośliwych danych. Wystarczająco wykwalifikowany może zidentyfikować używany protokół komunikacyjny i zmodyfikować przepływu danych, np. podszywając się pod jeden z punktów końcowych, wysyłając specjalnie przygotowane ładunki. Ten rodzaj złośliwego ruchu może osłabić skuteczność do funkcji aplikacji i, w najgorszym przypadku, spowodować nieoczekiwane działanie aplikacji i urządzenia oraz ataki typu DoS, polecenia wstrzykiwanie różnych danych i przejęcie urządzenia.
Środki zaradcze
Android udostępnia deweloperom wydajne interfejsy API do zarządzania komunikacją między urządzeniami, np. klasyczny Bluetooth, BLE, NFC i Wifi P2P. Należy je połączyć ze starannie wdrożonym mechanizmem weryfikacji danych do dezynfekcji wszelkich danych wymienianych między dwoma urządzeniami.
Rozwiązanie to należy wdrożyć na poziomie aplikacji i powinno obejmować sprawdza, czy dane mają oczekiwaną długość, format i zawierają prawidłowy ładunek, który może zostać zinterpretowany przez aplikację.
Ten fragment kodu pokazuje przykładową logikę sprawdzania danych. Zostało to zaimplementowane na podstawie przykładu dla deweloperów Androida dotyczącego przesyłania danych przez 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;
}
}
Zagrożenie: wstrzyknięcie szkodliwych danych przez USB
Połączenia USB między 2 urządzeniami mogą być celem ataków złośliwych użytkowników zainteresowanych przechwyceniem komunikacji. W tym przypadku wymagane połączenie fizyczne stanowi dodatkową warstwę zabezpieczeń, ponieważ atakujący musi uzyskać dostęp do kabla łączącego terminale, aby móc podsłuchiwać wiadomości. Innym wektorem ataku są niesprawdzone urządzenia USB, które są celowo lub przypadkowo podłączone do urządzenia.
Jeśli aplikacja filtruje urządzenia USB za pomocą PID/VID do wyzwalania określonych funkcji w aplikacji, atakujący mogą manipulować danymi przesyłanymi kanału USB, podszywając się pod prawidłowe urządzenie. Ataki tego typu mogą umożliwiać złośliwym użytkownikom wysyłanie naciśnięć klawiszy na urządzenie lub wykonywanie działań w aplikacji, które w najgorszym przypadku mogą prowadzić do zdalnego uruchamiania kodu lub pobierania niechcianego oprogramowania.
Środki zaradcze
Należy wdrożyć logikę weryfikacji na poziomie aplikacji. Ta logika powinna filtrować dane wysyłane przez USB, sprawdzając, czy ich długość, format i zawartość do swojego przypadku użycia. Na przykład monitor tętna nie powinien wysyłania poleceń naciśnięć klawiszy.
Ponadto, jeśli to możliwe, należy wziąć pod uwagę ograniczenie liczba pakietów USB, które aplikacja może odebrać z urządzenia USB. Ten zapobiega przeprowadzaniu ataków złośliwych urządzeń, takich jak Gumowa Ducky.
Można to zrobić, tworząc nowy wątek do sprawdzania treści buforowanej, na przykład po 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();
}
}
Określone zagrożenia
W tej sekcji zebraliśmy zagrożenia, które wymagają niestandardowych strategii łagodzenia lub zostały złagodzone na określonym poziomie pakietu SDK.
Zagrożenie: Bluetooth – nieprawidłowy czas wykrycia
Jak wspomnieliśmy w dokumentacji dotyczącej Bluetootha dla deweloperów aplikacji na Androida,
konfigurując interfejs Bluetooth w aplikacji za pomocą
Metoda startActivityForResult(Intent, int)
umożliwiająca włączenie urządzenia
wykrywalność i ustawienie parametru EXTRA_DISCOVERABLE_DURATION
na zero
sprawia, że urządzenie jest wykrywalne, dopóki aplikacja jest uruchomiona
w tle lub na pierwszym planie. W przypadku klasycznej specyfikacji Bluetooth wykrywalne urządzenia stale nadają określone wiadomości, które umożliwiają innym urządzeniom pobieranie danych z urządzenia lub łączenie się z nim. W
w takim przypadku złośliwa osoba może przechwycić takie wiadomości i połączyć
w urządzeniach z systemem Android. Po nawiązaniu połączenia osoba przeprowadzająca atak może wykonać dalsze działania
takich jak kradzież danych, DoS czy wstrzykiwanie poleceń.
Środki zaradcze
Pole EXTRA_DISCOVERABLE_DURATION
nigdy nie powinno mieć wartości 0. Jeśli parametr EXTRA_DISCOVERABLE_DURATION
nie jest ustawiony, domyślnie urządzenia są wykrywalne przez 2 minuty. Maksymalna wartość, jaką można ustawić dla parametru EXTRA_DISCOVERABLE_DURATION
, to 2 godziny (7200 sekund). Jest
zalecamy ograniczenie możliwego czasu trwania do najkrótszego czasu
w zależności od konkretnego przypadku użycia.
Zagrożenie: NFC – sklonowane filtry intencji
Złośliwa aplikacja może zarejestrować filtry intencji, aby odczytać określone tagi NFC lub urządzenia z obsługą NFC. Filtry te mogą powielać filtry zdefiniowane przez która umożliwia hakerowi przeczytanie treści wymienionych danych NFC. Należy pamiętać, że gdy 2 aktywności określają te same filtry intencji dla określonego tagu NFC, wyświetla się wybór aktywności, więc użytkownik musi wybrać szkodliwą aplikację, aby atak zadziałał. Mimo to połączenie filtrów intencji z ukrywaniem nadal jest możliwe. Ten atak jest ma znaczenie tylko wtedy, gdy można uznać dane wymieniane za pomocą NFC bardzo czułą.
Środki zaradcze
Podczas implementowania funkcji odczytu NFC w aplikacji filtry intencji można używać razem z rekordami aplikacji na Androida (AAR). Osadzanie rekord AAR w komunikacie NDEF daje silną pewność, że tylko uruchamiana jest legalna aplikacja i powiązane z nią działanie obsługi NDEF. Zapobiegnie to typowym odczytywaniu przez niechciane aplikacje i działania tagów poufnych lub danych z urządzenia wymienianych za pomocą NFC.
Zagrożenie: NFC – brak weryfikacji wiadomości NDEF
Gdy urządzenie z Androidem otrzymuje dane z tagu NFC lub z obsługą NFC system automatycznie uruchomi aplikację lub aktywność skonfigurowana do obsługi wiadomości NDEF zawartej w pliku. Zgodnie z logiką stosowaną w aplikacji dane zawarte w lub otrzymany z urządzenia może być przesyłany do innych działań w celu wywołania dalsze działania, takie jak otwieranie stron internetowych.
Aplikacja, która nie weryfikuje treści wiadomości NDEF, może pozwolić atakującym na wstrzyknięcie złośliwych danych w aplikacji za pomocą urządzeń z obsługą NFC lub tagów NFC, co może spowodować nieoczekiwane działanie, w tym pobranie złośliwego pliku, wstrzyknięcie polecenia lub atak DoS.
Środki zaradcze
Przed wysłaniem otrzymanej wiadomości NDEF do jakiegokolwiek innego komponentu aplikacji dane w ramach domeny powinny być weryfikowane pod kątem określonego formatu i zawierać oczekiwanych informacji. Dzięki temu złośliwe dane nie są przekazywane do innych komponentów aplikacji bez filtrowania, co zmniejsza ryzyko nieoczekiwanego działania lub ataków z wykorzystaniem danych NFC z poza kontrolą.
Ten fragment kodu przedstawia przykładową metodę weryfikacji danych zaimplementowaną jako z komunikatem NDEF jako argumentem i jego indeksem w tablicy komunikatów. Zostało to zaimplementowane na podstawie przykładu dla deweloperów aplikacji na Androida, aby uzyskać dane ze zeskanowanego taga NFC NDEF:
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;
}
Materiały
- Uprawnienia czasu działania
- Przewodniki dotyczące połączeń
- Przykład
- BulkTransfer
- Kryptografia
- Konfigurowanie Bluetootha
- NFC Basis
- Rekordy aplikacji na Androida
- Klasyczna specyfikacja Bluetooth