Dziś wpis na nietypowy temat - aplikacje na Androida. A konkretnie odrobina o ich bezpieczeństwie.
Ściany mają uszy (i potrafią mówić)
Nie będę wnikał tu w szczegóły, ale krótkie wprowadzenie się przyda. Mamy więc Activity, z których składa się aplikacja (a przynajmniej jej GUI). Aplikacja może składać się z wielu takich elementów, może też korzystać z funkcji zdefiniowanych w innych aplikacjach. Do tego przydaje się Intent. Każda aplikacja w pliku AndroidManifest.xml umieszcza listę swoich Activity (i nie tylko, ja w tym kontekście warto jeszcze wspomnieć o Service). Domyślnie Activity i Service są "lokalne" dla danej aplikacji, ale jeśli zdefiniowany zostanie IntentFilter, to stają się one dostępne dla innych aplikacji w systemie. Jeśli chcemy ograniczyć jakie aplikacje powinny móc korzystać z komponentów naszej aplikacji, musimy ustalić odpowiednie permission.
Do czego służy Service? Ten cytat z dokumentacji jest chyba wystarczającym wyjaśnieniem:
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
Teraz załóżmy, że nasz użytkownik będąc na jakimś Activity chce wywołać operację, która może zająć wiele czasu i w szczególności może zakończyć się niepowodzeniem. By nie spowodować zamrożenia GUI (albo wkurzającej "kręcioły"), programista może tą funkcję zaimplementować w usłudze i z poziomu Activity wywołać tę usługę poprzez wywołanie funkcji startService. W szczególności może być tak, że wyśle on w tym miejscu Intent o określonej nazwie, a system wywoła usługę, która ma zarejestrowany odpowiedni IntentFilter. I teraz ważna sprawa Intent może być wysłany "rozgłoszeniowo".
W jaki sposób rezultat pracy usługi może być zwrócony do Activity? Na przykład wysyłając broadcast, który w pewnych przypadkach może trafić do wszystkich aplikacji na urządzeniu, które zarejestrowały odpowiednie filtry (patrz BroadcastReceiver: Security).
Widzicie już potencjalny problem? Zresztą ta kwestia jest dość dokładnie pokazana w dokumentacji Androida, ale na wszelki wypadek:
- zewnętrzna aplikacja może wywołać usługę w "naszej" aplikacji,
- zewnętrzna aplikacja może otrzymywać komunikaty z rezultatem wywołania usługi,
Oczywiście może, ale wcale nie musi, muszą ku temu zaistnieć sprzyjające okoliczności (błędy).
Do czego to może prowadzić? Wyobraźmy sobie aplikację, niech będzie to aplikacja bankowości internetowej, która skonstruowana jest w następujący sposób:
- W Activity nie ma żadnego kodu do komunikacji z serwerem,
- Komunikacja z serwerem jest realizowana w Service,
- Po logowaniu tworzony jest obiekt odpowiadający za komunikację poszczególnych Service z serwerem,
- Activity wywołuje Service wysyłając odpowiedni Intent,
- Service zwraca rezultat wywołania wysyłając odpowiedni Intent (broadcast),
Do czasu, gdy Intenty wymienione między Activity i Service są lokalne (patrz np. LocalBroadcastManager) a usługi nie są eksportowane (patrz: android:exported), problemu w zasadzie nie ma. Co jednak, jeśli zaistnieją "sprzyjające okoliczności"?
Potencjalny scenariusz ataku jest prosty. Zewnętrzna aplikacja może:
- zarejestrować BroadcastReceiver dla interesujących ją Intent,
- wołać pewne funkcje API poprzez wywoływanie Service z odpowiednim Intent,
Skutkiem takiej podatności będzie co najmniej wyciek danych. Piszę co najmniej, bo teoretycznie mogę wyobrazić sobie sytuację, w której korzystając z socjotechniki wroga aplikacja skłoni użytkownika do podania kodu autoryzującego transakcję.
Cała ta sytuacja kojarzy mi się z następującym (hipotetycznym?) przykładem. W pokoju pracuje dwie osoby, "wywołują" wzajemnie swoje "usługi" rozmawiając ze sobą, a rozmawiają na tematy "wrażliwe" i mają możliwość wywołania "wrażliwych" operacji (np. "odblokuj mi konto X i ustaw hasło na Y"). I tak żyją sobie spokojnie przez długi czas, aż pewnego dnia w pokoju pojawia się trzecia osoba. Sposób pracy dwóch "starych" osób nie ulega zmianie, ale zmieniło się środowisko. Nowa osoba może po pierwsze uzyskać interesującą wiedzę tylko słuchając rozmowy (taki BroadcastReceiver). Może też spróbować "ataku aktywnego", czyli po prostu spróbować poprosić kogoś o zrobienie czegoś, do czego ten nowy pracownik nie ma uprawnień. Zadziała? Może, ale nie musi. Nie musi, bo my ludzie mamy często domyślnie włączony mechanizm uwierzytelnienia oparty na biometrii - rozpoznajemy osobę po głosie. To dlatego w przypadku przekrętu "na wnuczka" pojawia się chore gardło.