Android ma wbudowane funkcje zabezpieczeń, które znacznie zmniejszają częstotliwość występowania problemów z bezpieczeństwem aplikacji i ich wpływ na nią. System jest tak zaprojektowany, aby umożliwić tworzenie aplikacji z domyślnymi uprawnieniami systemu i plików oraz unikanie trudnych decyzji dotyczących zabezpieczeń.
Te podstawowe funkcje zabezpieczeń pomagają tworzyć bezpieczne aplikacje:
- Piaskownica aplikacji na Androida, która izoluje dane aplikacji i wykonanie kodu od innych aplikacji.
- Platforma aplikacji z solidnymi implementacjami typowych funkcji bezpieczeństwa, takich jak kryptografia, uprawnienia i bezpieczna komunikacja między procesami (IPC).
- Technologie takie jak adresowanie z losowaniem rozkładu (ASLR), NX (niewykonywanie), ProPolice, safe_iop, OpenBSD
dlmalloc
icalloc
oraz Linuxmmap_min_addr
mają na celu ograniczenie ryzyka związanego ze zwykłymi błędami zarządzania pamięcią. - Uprawnienia przyznane przez użytkownika, które ograniczają dostęp do funkcji systemu i danych użytkownika.
- Uprawnienia zdefiniowane przez aplikację, które umożliwiają kontrolowanie danych aplikacji na poziomie poszczególnych aplikacji.
Ważne jest, aby zapoznać się ze sprawdzonymi metodami zabezpieczania Androida opisanymi na tej stronie. Stosowanie tych praktyk jako ogólnych nawyków związanych z programowaniem pomoże Ci uniknąć przypadkowego wprowadzenia problemów z bezpieczeństwem, które mogą negatywnie wpłynąć na użytkowników.
Uwierzytelnianie
Uwierzytelnianie jest warunkiem wstępnym wielu kluczowych operacji bezpieczeństwa. Aby kontrolować dostęp do chronionych zasobów, takich jak dane użytkownika, funkcje aplikacji i inne zasoby, musisz dodać uwierzytelnianie do aplikacji na Androida.
Możesz ulepszyć proces uwierzytelniania użytkownika, integrując aplikację z Menedżerem danych logowania. Credential Manager to biblioteka Jetpack na Androida, która umożliwia korzystanie z interfejsu API w przypadku większości głównych metod uwierzytelniania, w tym kluczy dostępu, haseł i rozwiązań logowania federacyjnego, takich jak Zaloguj się przez Google.
Aby zwiększyć bezpieczeństwo aplikacji, rozważ dodanie metod uwierzytelniania biometrycznego, takich jak skanowanie odcisków palców czy rozpoznawanie twarzy. Dodanie uwierzytelniania biometrycznego może być dobrym rozwiązaniem w przypadku aplikacji związanych z finansami, opieką zdrowotną lub zarządzaniem tożsamością.
Usługa autouzupełniania na Androidzie może ułatwić proces rejestracji i logowania, zmniejszając liczbę błędów i problemów użytkowników. Automatyczne wypełnianie jest zintegrowane z menedżerami haseł, co pozwala użytkownikom wybierać złożone, losowe hasła, które można łatwo i bezpiecznie przechowywać oraz pobierać.
Integralność aplikacji
Interfejs Play Integrity API pomaga sprawdzać, czy interakcje i żądania serwera pochodzą z autentycznego pliku binarnego aplikacji na oryginalnym urządzeniu z Androidem. Dzięki wykrywaniu potencjalnie ryzykownych i fałszywych interakcji, takich jak te pochodzące z zmodyfikowanych wersji aplikacji i niebezpiecznych środowisk, serwer backendu aplikacji może podejmować odpowiednie działania, które mają na celu zapobieganie atakom i ograniczanie nadużyć.
Przechowywanie danych
Najczęstszym problemem związanym z bezpieczeństwem aplikacji na Androida jest to, czy dane zapisane na urządzeniu są dostępne dla innych aplikacji. Dane na urządzeniu można zapisywać na 3 podstawowe sposoby:
- Pamięć wewnętrzna
- Pamięć zewnętrzna
- Dostawcy treści
W sekcjach poniżej opisaliśmy problemy z zabezpieczeniami związane z każdym z tych podejść.
Pamięć wewnętrzna
Domyślnie pliki utworzone w pamięci wewnętrznej są dostępne tylko dla Twojej aplikacji. Android zapewnia tę ochronę i w większości przypadków jest to wystarczające.
Nie używaj przestarzałych trybów MODE_WORLD_WRITEABLE
i MODE_WORLD_READABLE
w plikach IPC. Nie umożliwiają ograniczenia dostępu do danych w przypadku konkretnych aplikacji i nie dają możliwości kontrolowania formatu danych. Jeśli chcesz udostępniać dane innym procesom aplikacji, rozważ użycie dostawcy treści, który oferuje uprawnienia odczytu i zapisu innym aplikacjom oraz może przyznawać uprawnienia dynamicznie w zależności od konkretnego przypadku.
Pamięć zewnętrzna
Pliki utworzone na pamięci zewnętrznej, takiej jak karta SD, są dostępne do odczytu i zapisu na całym świecie. Ponieważ użytkownik może usunąć zewnętrzną pamięć masową, a także zmodyfikować ją za pomocą dowolnej aplikacji, przechowuj w niej tylko informacje niepoufne.
Sprawdzaj dane wejściowe podczas obsługi danych ze zewnętrznego magazynu, tak jak w przypadku danych z niezaufanego źródła. Przed dynamicznym wczytywaniem nie przechowuj plików wykonywalnych ani plików klas na zewnętrznym urządzeniu do przechowywania danych. Jeśli aplikacja pobiera pliki wykonywalne z zewnętrznej pamięci masowej, upewnij się, że są one podpisane i zweryfikowane kryptograficznie przed wczytaniem dynamicznym.
Dostawcy treści
Dostawcy treści oferują uporządkowany mechanizm przechowywania, który może być ograniczony do Twojej aplikacji lub wyeksportowany, aby umożliwić dostęp innym aplikacjom. Jeśli nie chcesz udostępniać innym aplikacjom dostępu do ContentProvider
, zaznacz to jako android:exported=false
w pliku manifestu aplikacji. W przeciwnym razie ustaw atrybut android:exported
na true
, aby umożliwić innym aplikacjom dostęp do zapisanych danych.
Podczas tworzenia ContentProvider
, które ma być eksportowane do innych aplikacji, możesz określić jedno uprawnienie do odczytu i zapisu lub oddzielne uprawnienia do odczytu i zapisu. Ogranicz uprawnienia do tych, które są wymagane do wykonania danego zadania. Pamiętaj, że dodanie uprawnień w późniejszym czasie, aby udostępnić nową funkcję, jest zwykle łatwiejsze niż odebranie uprawnień i wpływ na obecnych użytkowników.
Jeśli korzystasz z usług dostawcy treści do udostępniania danych tylko w ramach swoich aplikacji, zalecamy użycie zestawu atrybutów android:protectionLevel
w celu ochrony danychsignature
. Uprawnienia na podstawie podpisu nie wymagają potwierdzenia przez użytkownika, dzięki czemu zapewniają lepsze wrażenia użytkownika i bardziej kontrolowany dostęp do danych dostawcy treści, gdy aplikacje uzyskujące dostęp do tych danych są podpisane tym samym kluczem.
Dostawcy treści mogą też przyznawać bardziej szczegółowy dostęp, deklarując atrybut android:grantUriPermissions
i używając flag FLAG_GRANT_READ_URI_PERMISSION
i FLAG_GRANT_WRITE_URI_PERMISSION
w obiekcie Intent
, który aktywuje komponent. Zakres tych uprawnień można dodatkowo ograniczyć za pomocą elementu <grant-uri-permission>
.
Podczas uzyskiwania dostępu do dostawcy treści używaj metod zapytań z parametrami, takich jak query
, update
i delete()
, aby uniknąć potencjalnego wstrzyknięcia kodu SQL z niezaufanego źródła. Pamiętaj, że używanie metod parametrycznych nie wystarczy, jeśli argument selection
jest tworzony przez złączenie danych użytkownika przed przesłaniem ich do metody.
Nie daj się zwieść fałszywemu poczuciu bezpieczeństwa związanemu z uprawnieniami do zapisu. Uprawnienie write umożliwia stosowanie instrukcji SQL, które umożliwiają potwierdzanie niektórych danych za pomocą klauzul WHERE
w kreacji i analizowanie wyników. Na przykład atakujący może sprawdzić obecność określonego numeru telefonu w dzienniku połączeń, modyfikując wiersz tylko wtedy, gdy ten numer już istnieje. Jeśli dane dostawcy treści mają przewidywalną strukturę, uprawnienia do zapisu mogą być równoznaczne z dostępem do odczytu i zapisu.
Uprawnienia
Aplikacje na Androida są od siebie odseparowane, dlatego muszą wyraźnie udostępniać zasoby i dane. Robią to, deklarując uprawnienia, których potrzebują do dodatkowych funkcji niedostępnych w ramach podstawowej piaskownicy, w tym dostępu do funkcji urządzenia, takich jak kamera.
Prośby o uprawnienia
Zmniejsz liczbę uprawnień, o które prosi aplikacja. Ograniczenie dostępu do wrażliwych uprawnień zmniejsza ryzyko ich przypadkowego niewłaściwego użycia, zwiększa liczbę użytkowników i czyni aplikację mniej podatną na ataki. Jeśli danego uprawnienia nie trzeba, aby aplikacja działała, nie proś o nie. Zapoznaj się z przewodnikiem oceniania, czy aplikacja musi deklarować uprawnienia.
Jeśli to możliwe, zaprojektuj aplikację tak, aby nie wymagała żadnych uprawnień. Zamiast prosić o dostęp do informacji o urządzeniu, aby utworzyć unikalny identyfikator, możesz utworzyć identyfikator UUID dla swojej aplikacji. (więcej informacji znajdziesz w sekcji dotyczącej danych użytkownika). Zamiast korzystać z zewnętrznego miejsca na dane (które wymaga uprawnień), możesz przechowywać dane w pamięci wewnętrznej.
Oprócz żądania uprawnień aplikacja może używać elementu <permission>
, aby chronić interfejs IPC, który jest newralgiczny pod względem bezpieczeństwa i jest dostępny dla innych aplikacji, takich jak ContentProvider
. Ogólnie zalecamy używanie innych mechanizmów kontroli dostępu niż wymagające potwierdzenia przez użytkownika, ponieważ uprawnienia mogą wprowadzać użytkowników w błąd. Rozważ użycie na przykład poziomu ochrony sygnatury w przypadku uprawnień do komunikacji IPC między aplikacjami udostępnianymi przez jednego dewelopera.
Nie udostępniaj danych chronionych przez uprawnienia. Dzieje się tak, gdy aplikacja udostępnia dane przez interfejs IPC, które są dostępne tylko dlatego, że aplikacja ma do nich dostęp. Klienci interfejsu IPC Twojej aplikacji mogą nie mieć tych samych uprawnień dostępu do danych. Więcej informacji o częstotliwości występowania tego problemu i jego potencjalnych skutkach znajdziesz w artykule Permission Re-Delegation: Attacks and Defenses (Przedelegowanie uprawnień: ataki i obrona), opublikowanym w USENIX.
Definicje uprawnień
Określ najmniejszy zestaw uprawnień, który spełnia Twoje wymagania dotyczące zabezpieczeń. Tworzenie nowych uprawnień jest stosunkowo rzadkie w przypadku większości aplikacji, ponieważ uprawnienia zdefiniowane przez system obejmują wiele sytuacji. W razie potrzeby sprawdzaj dostęp za pomocą istniejących uprawnień.
Jeśli potrzebujesz nowego uprawnienia, zastanów się, czy możesz wykonać swoje zadanie przy poziomie ochrony podpisu. Użytkownik widzi uprawnienia związane z podpisem i może zezwolić na dostęp tylko aplikacjom podpisanym przez tego samego dewelopera, który podpisał aplikację sprawdzającą uprawnienia.
Jeśli nadal wymagane jest utworzenie nowego uprawnienia, zadeklaruj je w pliku manifestu aplikacji za pomocą elementu <permission>
. Aplikacje korzystające z nowego uprawnienia mogą się do niego odwoływać, dodając element <uses-permission>
do plików manifestu. Uprawnienia możesz też dodawać dynamicznie, używając metody addPermission()
.
Jeśli utworzysz uprawnienie z poziomem ochrony niebezpieczny, musisz wziąć pod uwagę kilka kwestii:
- Uprawnienie musi zawierać ciąg znaków, który w zwięzły sposób przekazuje użytkownikowi decyzję dotyczącą zabezpieczeń, którą musi podjąć.
- Ciąg znaków uprawnień musi być zlokalizowany na wiele języków.
- Użytkownicy mogą nie chcieć instalować aplikacji, ponieważ uprawnienie jest mylące lub postrzegane jako ryzykowne.
- Aplikacje mogą prosić o uprawnienia, gdy twórca uprawnienia nie został zainstalowany.
Każde z nich stanowi dla Ciebie jako dewelopera poważne wyzwanie nietechniczne, a zarazem wprowadza użytkowników w błąd. Dlatego nie zalecamy używania niebezpiecznego poziomu uprawnień.
Sieć
Transakcje sieciowe są z samej swojej natury niebezpieczne, ponieważ obejmują przesyłanie danych, które mogą być prywatne dla użytkownika. Użytkownicy coraz bardziej zdają sobie sprawę z problemów związanych z prywatnością na urządzeniach mobilnych, zwłaszcza gdy urządzenie wykonuje transakcje sieciowe. Dlatego bardzo ważne jest, aby Twoja aplikacja zawsze stosowała sprawdzone metody zapewniające bezpieczeństwo danych użytkownika.
Sieć IP
Sieci na Androidzie nie różnią się znacząco od innych środowisk Linuksa. Najważniejsze jest zapewnienie, aby w przypadku danych poufnych używano odpowiednich protokołów, takich jak HttpsURLConnection
w przypadku bezpiecznego ruchu internetowego. Używaj protokołu HTTPS zamiast HTTP wszędzie tam, gdzie serwer obsługuje HTTPS, ponieważ urządzenia mobilne często łączą się z sieciami, które nie są zabezpieczone, takimi jak publiczne hotspoty Wi-Fi.
Za pomocą klasy SSLSocket
można łatwo zaimplementować uwierzytelnioną, zaszyfrowaną komunikację na poziomie gniazda. Urządzenia z Androidem często łączą się z niezabezpieczonymi sieciami bezprzewodowymi za pomocą Wi-Fi, dlatego zdecydowanie zalecamy korzystanie z bezpiecznych sieci w przypadku wszystkich aplikacji, które komunikują się przez sieć.
Niektóre aplikacje używają portów sieciowych localhost do obsługi poufnych interfejsów IPC. Nie stosuj tego podejścia, ponieważ te interfejsy są dostępne dla innych aplikacji na urządzeniu. Zamiast tego użyj mechanizmu IPC Androida, w którym możliwe jest uwierzytelnianie, np. za pomocą Service
. Powiązanie z nieokreślonym adresem IP INADDR_ANY
jest gorsze niż użycie pętli zwrotnej, ponieważ pozwala aplikacji odbierać żądania z dowolnego adresu IP.
Upewnij się, że nie ufasz danym pobieranym z HTTP ani z innych niezabezpieczonych protokołów. Obejmuje to weryfikację danych wejściowych w WebView
i wszystkich odpowiedzi na intencje wysyłane przez HTTP.
Sieci telefoniczne
Protokół SMS został zaprojektowany głównie do komunikacji między użytkownikami i nie nadaje się do aplikacji, które chcą przesyłać dane. Ze względu na ograniczenia SMS-ów zalecamy używanie Komunikacji w chmurze Firebase (FCM) i sieci IP do wysyłania wiadomości danych z serwera internetowego do aplikacji na urządzeniu użytkownika.
Pamiętaj, że SMS-y nie są szyfrowane ani silnie uwierzytelniane na poziomie sieci ani urządzenia. W szczególności każdy odbiorca SMS-a powinien się spodziewać, że SMS został wysłany do Twojej aplikacji przez złośliwego użytkownika. Nie polegaj na nieautoryzowanych danych SMS-owych do wykonywania poleceń dotyczących informacji poufnych. Pamiętaj też, że SMS-y mogą być podmieniane lub przechwytywane w sieci. Na urządzeniu z Androidem wiadomości SMS są przesyłane jako intencje przesyłania, dzięki czemu mogą być odczytywane lub przechwytywane przez inne aplikacje, które mają uprawnienia READ_SMS
.
weryfikacja danych wejściowych,
Niewystarczająca weryfikacja danych wejściowych to jeden z najczęstszych problemów z bezpieczeństwem, który dotyczy aplikacji niezależnie od platformy, na której są one uruchamiane. Android ma środki zaradcze na poziomie platformy, które zmniejszają narażenie aplikacji na problemy z weryfikacją danych wejściowych. Zalecamy korzystanie z tych funkcji, jeśli to możliwe. Zalecamy też używanie języków bezpiecznych pod względem typu, aby zmniejszyć prawdopodobieństwo wystąpienia problemów z weryfikacją danych wejściowych.
Jeśli używasz kodu natywnego, wszelkie dane odczytane z plików, otrzymane przez sieć lub otrzymane z IPC mogą potencjalnie powodować problemy z bezpieczeństwem. Najczęstsze problemy to przepełnienie bufora, użycie po zwolnieniu i błędy typu off-by-one. Android udostępnia wiele technologii, takich jak ASLR i DEP, które ograniczają możliwość wykorzystania tych błędów, ale nie rozwiązują podstawowego problemu. Możesz zapobiegać tym podatnościom, ostrożnie obsługując wskaźniki i zarządzając buforami.
Dynamiczne języki oparte na ciągach znaków, takie jak JavaScript i SQL, również są podatne na problemy z weryfikacją danych wejściowych z powodu znaków ucieczki i wstrzykiwania skryptów.
Jeśli używasz danych w zapytaniach przesyłanych do bazy danych SQL lub dostawcy treści, może to spowodować wstrzyknięcie kodu SQL. Najlepszym sposobem na ochronę jest używanie zapytań parametrycznych, o których mowa w sekcji o dostawcach treści. Ograniczenie uprawnień do odczytu lub zapisu może też zmniejszyć potencjalne szkody związane z wstrzyknięciem kodu SQL.
Jeśli nie możesz korzystać z funkcji bezpieczeństwa opisanych w tej sekcji, używaj uporządkowanych formatów danych i sprawdzaj, czy dane są zgodne z oczekiwanym formatem. Chociaż blokowanie określonych znaków lub ich zastępowanie może być skuteczną strategią, w praktyce te techniki są podatne na błędy, dlatego zalecamy ich unikanie, jeśli to możliwe.
Dane użytkownika
Najlepszym podejściem do zapewnienia bezpieczeństwa danych użytkowników jest minimalizowanie korzystania z interfejsów API, które mają dostęp do informacji poufnych lub danych osobowych. Jeśli masz dostęp do danych użytkownika, unikaj ich przechowywania lub przesyłania. Zastanów się, czy logikę aplikacji można zaimplementować, używając haszowanej lub nieodwracalnej formy danych. Na przykład aplikacja może używać hasha adresu e-mail jako klucza podstawowego, aby uniknąć przesyłania lub przechowywania tego adresu. Pozwala to zmniejszyć ryzyko przypadkowego ujawnienia danych i zwiększyć szanse na to, że atakujący nie spróbują wykorzystać Twojej aplikacji.
Potwierdzaj tożsamość użytkownika, gdy wymagany jest dostęp do danych prywatnych, i używaj nowoczesnych metod uwierzytelniania, takich jak klucze dostępu i menedżer danych logowania. Jeśli Twoja aplikacja potrzebuje dostępu do danych osobowych, pamiętaj, że w niektórych jurysdykcjach może być wymagane udostępnienie polityki prywatności wyjaśniającej sposób wykorzystywania i przechowywania tych danych. Aby uprościć zgodność z wymogami, stosuj sprawdzoną metodę minimalizowania dostępu do danych użytkowników.
Zastanów się też, czy Twoja aplikacja może przypadkowo ujawnić dane osobowe innym podmiotom, np. komponentom zewnętrznym służącym do wyświetlania reklam lub usługom zewnętrznym używanym przez aplikację. Jeśli nie wiesz, dlaczego dany komponent lub usługa wymagają danych osobowych, nie podawaj ich. Ogólnie rzecz biorąc, ograniczenie dostępu aplikacji do danych osobowych zmniejsza ryzyko wystąpienia problemów w tym zakresie.
Jeśli aplikacja wymaga dostępu do danych wrażliwych, zastanów się, czy musisz je przesyłać na serwer, czy możesz wykonać tę operację po stronie klienta. Aby uniknąć przesyłania danych użytkownika, rozważ uruchomienie kodu korzystającego z danych wrażliwych na kliencie. Upewnij się też, że nie udostępniasz danych użytkownika innym aplikacjom na urządzeniu za pomocą zbyt liberalnego interfejsu IPC, plików do zapisu dla wszystkich użytkowników lub gniazd sieciowych. Nadmiernie liberalne IPC to szczególny przypadek wycieku danych chronionych przez uprawnienia, o którym mowa w sekcji Prośby o uprawnienia.
Jeśli wymagany jest globalnie unikalny identyfikator (GUID), utwórz duży, unikalny numer i go zapisz. Nie używaj identyfikatorów telefonów, takich jak numer telefonu czy numer IMEI, które mogą być powiązane z danymi osobowymi. Ten temat jest omawiany bardziej szczegółowo na stronie sprawdzonych metod dotyczących unikalnych identyfikatorów.
Zachowaj ostrożność podczas zapisywania danych w logach na urządzeniu. Na Androidzie logi są wspólnym zasobem i są dostępne dla aplikacji z uprawnieniami READ_LOGS
. Mimo że dane dziennika telefonu są tymczasowe i usuwane po ponownym uruchomieniu, nieodpowiednie rejestrowanie informacji o użytkowniku może spowodować niezamierzone ujawnienie tych danych innym aplikacjom. Oprócz nierejestrowania danych PII ograniczaj użycie logów w aplikacji produkcyjnej. Aby łatwo to zaimplementować, użyj flag debugowania i niestandardowych klas Log
z łatwo konfigurowalnymi poziomami rejestrowania.
WebView | komponent WebView
Ponieważ WebView
korzysta z treści internetowych, które mogą zawierać kod HTML i JavaScript, nieprawidłowe użycie może spowodować typowe problemy z bezpieczeństwem internetowym, takie jak cross-site scripting (wstrzykiwanie kodu JavaScript). Android zawiera wiele mechanizmów, które ograniczają zakres tych potencjalnych problemów, ograniczając możliwości WebView
do minimalnej funkcjonalności wymaganej przez aplikację.
Jeśli Twoja aplikacja nie używa bezpośrednio JavaScriptu w ramach WebView
, nie wywołuj funkcji setJavaScriptEnabled
. Niektóre przykładowe kody używają tej metody. Jeśli używasz przykładowego kodu w produkcyjnej aplikacji, usuń wywołanie tej metody, jeśli nie jest ono wymagane. Domyślnie WebView
nie wykonuje kodu JavaScript, więc ataki typu XSS są niemożliwe.
Używaj addJavaScriptInterface()
ze szczególną ostrożnością, ponieważ pozwala ono wywoływać operacje JavaScript, które są zwykle zarezerwowane dla aplikacji na Androida. Jeśli z niej korzystasz, udostępniaj addJavaScriptInterface()
tylko stronom internetowym, z których dane są wiarygodne. Jeśli dozwolisz na niesprawdzone dane wejściowe, niesprawdzony kod JavaScript może wywoływać metody Androida w Twojej aplikacji. Ogólnie zalecamy udostępnianie addJavaScriptInterface()
tylko kodowi JavaScript zawartemu w pliku APK aplikacji.
Jeśli Twoja aplikacja uzyskuje dostęp do danych wrażliwych za pomocą WebView
, rozważ użycie metody clearCache()
, aby usunąć wszystkie pliki przechowywane lokalnie. Możesz też użyć nagłówków po stronie serwera, takich jak no-store
, aby wskazać, że aplikacja nie powinna przechowywać w pamięci podręcznej określonych treści.
Urządzenia z platformami starszymi niż Android 4.4 (poziom interfejsu API 19) korzystają z wersji webkit
, która ma wiele problemów z bezpieczeństwem. Aby obejść ten problem, jeśli aplikacja działa na tych urządzeniach, musi potwierdzić, że obiekty WebView
wyświetlają tylko zaufane treści. Aby mieć pewność, że Twoja aplikacja nie jest narażona na potencjalne luki w zabezpieczeniach protokołu SSL, użyj obiektu Provider
, który można aktualizować, zgodnie z opisem w artykule Aktualizuj dostawcę zabezpieczeń, aby chronić się przed atakami na SSL. Jeśli Twoja aplikacja musi renderować treści z otwartej sieci, rozważ udostępnienie własnego renderera, aby można było aktualizować go najnowszymi poprawkami bezpieczeństwa.
Żądania dotyczące danych logowania
Żądania danych logowania to wektor ataku. Oto kilka wskazówek, które pomogą Ci zwiększyć bezpieczeństwo żądań danych logowania w aplikacjach na Androida.
Minimalizowanie narażenia danych logowania
- Unikaj zbędnych próśb o dane logowania. Aby utrudnić ataki phishingowe i zminimalizować ich skuteczność, ograniczaj częstotliwość proszenia o dane logowania. Zamiast tego użyj tokena autoryzacji i odśwież go. Proś o minimalną liczbę informacji o poświadczeniach tożsamości niezbędnych do uwierzytelniania i autoryzacji.
- Przechowuj dane logowania w bezpiecznym miejscu. Użyj Menedżera danych logowania, aby włączyć uwierzytelnianie bez hasła za pomocą kluczy dostępu lub wdrożyć logowanie sfederowane za pomocą schematów takich jak Zaloguj się przez Google. Jeśli musisz używać tradycyjnego uwierzytelniania za pomocą hasła, nie przechowuj identyfikatorów użytkowników ani haseł na urządzeniu. Zamiast tego przeprowadź początkowe uwierzytelnianie za pomocą nazwy użytkownika i hasła podanych przez użytkownika, a następnie użyj krótkotrwałego tokena autoryzacji związanego z usługą.
- Ogranicz zakres uprawnień. Nie proś o szerokie uprawnienia do wykonania zadania, które wymaga tylko węższego zakresu.
- Ogranicz tokeny dostępu. Używaj operacji z tokenami krótkotrwałymi i wywołań interfejsu API.
- Ogranicz częstotliwość uwierzytelniania. Szybkie, kolejne żądania uwierzytelniania lub autoryzacji mogą być oznaką ataku siłowego. Ogranicz te współczynniki do rozsądczej częstotliwości, zachowując przy tym funkcjonalność i przyjazne użytkownikom wrażenia z korzystania z aplikacji.
Używanie bezpiecznego uwierzytelniania
- Wdrożenie kluczy dostępu. Włącz klucze dostępu jako bezpieczniejszą i łatwą w użyciu alternatywę dla haseł.
- Dodaj dane biometryczne. Zapewnij możliwość korzystania z uwierzytelniania biometrycznego, takiego jak rozpoznawanie odcisków palców czy twarzy, aby zwiększyć bezpieczeństwo.
- Użyj dostawców tożsamości sfederowanej. Credential Manager obsługuje dostawców uwierzytelniania federacyjnego, takich jak Zaloguj się przez Google.
- Szyfruj komunikację. Używaj protokołu HTTPS i podobnych technologii, aby chronić dane przesyłane przez aplikację przez sieć.
Zarządzanie kontem w sposób bezpieczny
- łączyć się z usługami dostępnymi dla wielu aplikacji za pomocą
AccountManager
. Aby wywołać usługę działającą w chmurze, użyj klasyAccountManager
. Nie przechowuj haseł na urządzeniu. - Po użyciu funkcji
AccountManager
do pobraniaAccount
użyj funkcjiCREATOR
, zanim przekażesz dane logowania, aby nie przekazać ich przez pomyłkę niewłaściwej aplikacji. - Jeśli dane logowania są używane tylko przez aplikacje utworzone przez Ciebie, możesz zweryfikować aplikację, która uzyskuje dostęp do
AccountManager
, za pomocącheckSignatures
. Jeśli dane logowania są używane tylko przez jedną aplikację, możesz je przechowywać wKeyStore
.
Zachowaj czujność
- Aktualizuj kod. Pamiętaj, aby aktualizować kod źródłowy, w tym biblioteki i zależne komponenty innych firm, aby chronić się przed najnowszymi lukami w zabezpieczeniach.
- Monitorowanie podejrzanej aktywności. Poszukaj potencjalnego niewłaściwego użycia, np. wzorców nadużyć autoryzacji.
- Sprawdź kod. Regularnie sprawdzaj kod źródłowy pod kątem zagrożeń związanych z bezpieczeństwem, aby wykrywać potencjalne problemy z żądaniem danych logowania.
Zarządzanie kluczami interfejsu API
Klucze interfejsu API są kluczowym elementem wielu aplikacji na Androida, ponieważ umożliwiają im dostęp do usług zewnętrznych i wykonywanie podstawowych funkcji, takich jak łączenie się z usługami mapowania, uwierzytelnianie i usługi pogodowe. Ujawnienie tych kluczy może mieć jednak poważne konsekwencje, w tym wycieki danych, nieautoryzowany dostęp i straty finansowe. Aby uniknąć takich sytuacji, programiści powinni stosować bezpieczne strategie obsługi kluczy interfejsu API w całym procesie rozwoju.
Aby chronić usługi przed niewłaściwym użyciem, należy starannie chronić klucze interfejsu API. Aby zabezpieczyć połączenie między aplikacją a usługą, która używa klucza interfejsu API, musisz zabezpieczyć dostęp do interfejsu API. Gdy aplikacja jest skompilowana, a jej kod źródłowy zawiera klucze interfejsu API, atakujący może zdekompilować aplikację i znaleźć te zasoby.
Ta sekcja jest przeznaczona dla 2 grup programistów aplikacji na Androida: tych, którzy współpracują z zespołami infrastruktury nad ulepszaniem ścieżki ciągłego dostarczania, oraz tych, którzy wdrażają samodzielne aplikacje w Sklepie Play. W tej sekcji znajdziesz sprawdzone metody postępowania z kluczami interfejsu API, dzięki którym aplikacja będzie mogła bezpiecznie komunikować się z usługami.
Generowanie i przechowywanie
Deweloperzy powinni traktować przechowywanie kluczy interfejsu API jako kluczowy element ochrony danych i prywatności użytkowników, stosując podejście obrony wielowarstwowej.
Bezpieczne przechowywanie kluczy
Aby zapewnić optymalne bezpieczeństwo zarządzania kluczami, użyj magazynu kluczy Androida i zaszyfruj przechowywane klucze za pomocą niezawodnego narzędzia, takiego jak Tink Java.
Wykluczenie kontroli wersji
Nigdy nie zapisuj kluczy interfejsu API w repozytorium kodu źródłowego. Dodanie kluczy interfejsu API do kodu źródłowego może spowodować ich ujawnienie w publicznych repozytoriach, udostępnionych przykładach kodu i plikach udostępnionych przez przypadek. Zamiast tego używaj wtyczek Gradle, takich jak secrets-gradle-plugin, do pracy z kluczami interfejsu API w projekcie.
Klucze specyficzne dla środowiska
Jeśli to możliwe, używaj oddzielnych kluczy API w środowiskach programowania, testowania i produkcji. Używaj kluczy specyficznych dla środowiska, aby je od siebie odseparować. Pozwoli Ci to zmniejszyć ryzyko ujawnienia danych produkcyjnych i wyłączyć klucze zagrożone bez wpływu na środowisko produkcyjne.
Korzystanie i kontrola dostępu
Bezpieczne metody korzystania z kluczy interfejsu API są niezbędne do ochrony interfejsu API i użytkowników. Oto jak przygotować klucze, aby zapewnić optymalną ochronę:
- Wygeneruj unikalne klucze dla każdej aplikacji: używaj oddzielnych kluczy interfejsu API dla każdej aplikacji, aby łatwiej identyfikować i izolować skompromitowany dostęp.
- Wdróż ograniczenia adresów IP: jeśli to możliwe, ogranicz użycie klucza interfejsu API do określonych adresów IP lub zakresów adresów IP.
- Ogranicz użycie klucza w aplikacji mobilnej: ogranicz użycie klucza interfejsu API do określonych aplikacji mobilnych, zgrupowanych z kluczem lub używając certyfikatów aplikacji.
- Rejestrowanie i monitorowanie podejrzanej aktywności: wdrożenie mechanizmów rejestrowania i monitorowania wykorzystania interfejsu API w celu wykrywania podejrzanej aktywności i zapobiegania potencjalnemu nadużyciu.
Uwaga: usługa powinna udostępniać funkcje umożliwiające ograniczenie kluczy do konkretnego pakietu lub platformy. Na przykład interfejs API Google Maps ogranicza dostęp do klucza według nazwy pakietu i klucza podpisywania.
OAuth 2.0 zapewnia ramy do autoryzowania dostępu do zasobów. Określa on standardy interakcji klientów i serwerów oraz umożliwia bezpieczne autoryzowanie. Za pomocą OAuth 2.0 możesz ograniczyć użycie klucza API do określonych klientów i zdefiniować zakres dostępu, tak aby każdy klucz API miał tylko minimalny poziom dostępu wymagany do jego przeznaczenia.
Rotacja i wygaśnięcie klucza
Aby zmniejszyć ryzyko nieautoryzowanego dostępu przez nieodkryte luki w zabezpieczeniach interfejsu API, należy regularnie zmieniać klucze interfejsu API. Norma ISO 27001 określa ramy zgodności dotyczące częstotliwości rotacji kluczy. W większości przypadków okres rotacji kluczy powinien wynosić od 90 dni do 6 miesięcy. Wdrożenie niezawodnego systemu zarządzania kluczami może pomóc Ci usprawnić te procesy i zwiększyć wydajność rotacji kluczy oraz ich wygasania.
Ogólne sprawdzone metody
- Używanie protokołu SSL/HTTPS: zawsze używaj komunikacji HTTPS do szyfrowania żądań interfejsu API.
- Przypinanie certyfikatów: aby zapewnić dodatkową warstwę zabezpieczeń, możesz wdrożyć przypinanie certyfikatów, aby sprawdzić, które certyfikaty są uważane za ważne.
- Weryfikuj i sterylizuj dane wejściowe użytkownika: weryfikuj i sterylizuj dane wejściowe użytkownika, aby zapobiec atakom polegającym na wstrzykiwaniu kodu, które mogłyby ujawnić klucze interfejsu API.
- Stosuj sprawdzone metody dotyczące bezpieczeństwa: w procesie tworzenia oprogramowania stosuj ogólne sprawdzone metody dotyczące bezpieczeństwa, w tym techniki bezpiecznego kodowania, przeglądy kodu i skanowanie pod kątem podatności.
- Bądź na bieżąco: bądź na bieżąco z najnowszymi zagrożeniami dla bezpieczeństwa i najlepszymi rozwiązaniami dotyczącymi zarządzania kluczami interfejsu API.
- Aktualne pakiety SDK: upewnij się, że pakiety SDK i biblioteki są zaktualizowane do najnowszej wersji.
Kryptografia
Oprócz zapewniania izolacji danych, obsługi szyfrowania całego systemu plików i zapewniania bezpiecznych kanałów komunikacji Android oferuje też szeroki zakres algorytmów do ochrony danych za pomocą kryptografii.
Sprawdź, których dostawców zabezpieczeń Java Cryptography Architecture (JCA) używa Twoje oprogramowanie. Spróbuj użyć najwyższego poziomu implementacji istniejącego już frameworka, który może obsługiwać Twoje zastosowanie. W razie potrzeby użyj dostawców udostępnionych przez Google w kolejności określonej przez Google.
Jeśli chcesz bezpiecznie pobrać plik z znanej lokalizacji w sieci, możesz użyć prostego identyfikatora URI HTTPS, który nie wymaga znajomości szyfrowania. Jeśli potrzebujesz bezpiecznego tunelu, zamiast pisać własny protokół, rozważ użycie HttpsURLConnection
lub SSLSocket
. Jeśli używasz SSLSocket
, pamiętaj, że nie weryfikuje on nazwy hosta. Zapoznaj się z ostrzeżeniami dotyczącymi bezpośredniego korzystania z funkcji SSLSocket
.
Jeśli okaże się, że musisz zaimplementować własny protokół, nie stosuj własnych algorytmów szyfrowania. Używanie istniejących algorytmów kryptograficznych, takich jak implementacje AES i RSA udostępnione w klasie Cipher
.
Dodatkowo zastosuj te sprawdzone metody:
- Używaj 256-bitowego szyfrowania AES do celów komercyjnych. (jeśli nie jest dostępny, użyj 128-bitowego szyfrowania AES).
- Do szyfrowania wykorzystującego krzywe eliptyczne (EC) używaj kluczy publicznych o rozmiarze 224 lub 256 bitów.
- Dowiedz się, kiedy używać trybów blokowania CBC, CTR lub GCM.
- Unikaj ponownego używania IV lub licznika w trybie CTR. Upewnij się, że są one losowe pod względem kryptograficznym.
- Podczas korzystania z szyfrowania należy zastosować integralność za pomocą trybu CBC lub CTR z jedną z tych funkcji:
- HMAC-SHA1
- HMAC-SHA-256
- HMAC-SHA-512
- Tryb GCM
Aby zainicjować klucze kryptograficzne wygenerowane przez KeyGenerator
, użyj bezpiecznego generatora liczb losowych SecureRandom
. Używanie klucza, który nie został wygenerowany za pomocą bezpiecznego generatora liczb losowych, znacznie osłabia skuteczność algorytmu i może umożliwiać ataki offline.
Jeśli klucz ma być przechowywany i używany wielokrotnie, użyj mechanizmu, takiego jak KeyStore
, który umożliwia długoterminowe przechowywanie i pobieranie kluczy kryptograficznych.
Komunikacja między procesami
Niektóre aplikacje próbują stosować IPC za pomocą tradycyjnych technik Linuksa, takich jak gniazda sieciowe i udostępnione pliki. Zamiast tego zalecamy korzystanie z funkcji systemu Android do komunikacji między procesami, takich jak Intent
, Binder
lub Messenger
z Service
i BroadcastReceiver
. Mechanizmy IPC Androida umożliwiają weryfikację tożsamości aplikacji łączącej się z IPC oraz konfigurowanie zasad zabezpieczeń dla każdego mechanizmu IPC.
Wiele elementów zabezpieczeń jest wspólnych dla różnych mechanizmów IPC. Jeśli Twój mechanizm IPC nie jest przeznaczony do użytku przez inne aplikacje, w elemencie manifestu komponentu ustaw atrybut android:exported
na false
, na przykład w elemencie <service>
. Jest to przydatne w przypadku aplikacji, które składają się z wielu procesów w ramach tego samego identyfikatora UID, lub gdy w późnym etapie rozwoju zdecydujesz się, że nie chcesz udostępniać funkcji jako IPC, ale nie chcesz też ponownie pisać kodu.
Jeśli interfejs IPC jest dostępny dla innych aplikacji, możesz zastosować politykę bezpieczeństwa za pomocą elementu <permission>
. Jeśli IPC dotyczy aplikacji, które są Twoje i podpisane tym samym kluczem, użyj uprawnienia signature-level
w android:protectionLevel
.
Przeznaczenie
W przypadku aktywności i odbiorników transmisji preferowanym mechanizmem asynchronicznej komunikacji IPC na Androida są intencje. W zależności od wymagań aplikacji możesz użyć sendBroadcast
, sendOrderedBroadcast
lub jawnego intencjonalnego wywołania określonego komponentu aplikacji. Ze względów bezpieczeństwa zalecamy używanie wyraźnych intencji.
Uwaga: jeśli używasz intencjonalnego wiązania z **Service**
, aby zapewnić bezpieczeństwo aplikacji, użyj wyraźnego intencyjnego wiązania. Używanie domyślnego zamiaru do uruchamiania usługi jest niebezpieczne, ponieważ nie można mieć pewności, która usługa odpowie na zamiar, a użytkownik nie widzi, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołasz element **bindService()**
za pomocą niejawnego zamiaru.
Pamiętaj, że porządkowane transmisje mogą być odbierane przez odbiorcę, więc mogą nie być dostarczane do wszystkich aplikacji. Jeśli wysyłasz intencję, która musi zostać dostarczona do konkretnego odbiorcy, musisz użyć do niej domyślnej intencji, która określa odbiorcę według nazwy.
Nadawcy intencji mogą sprawdzić, czy odbiorca ma uprawnienia, określając uprawnienia inne niż null w wywołaniu metody. Tylko aplikacje z tym uprawnieniem otrzymują intencję. Jeśli dane w intencji przesyłania mogą być poufne, rozważ zastosowanie uprawnienia, aby zapewnić, że złośliwe aplikacje nie będą mogły rejestrować się do odbioru tych wiadomości bez odpowiednich uprawnień. W takich okolicznościach możesz też wywołać odbiornik bezpośrednio, zamiast wysyłać transmisję.
Uwaga: filtry intencji nie są funkcjami bezpieczeństwa. Komponenty mogą być wywoływane za pomocą wyraźnych intencji i mogą nie mieć danych zgodnych z filtrem intencji. Aby sprawdzić, czy jest ono prawidłowo sformatowane dla wywoływanego odbiorcy, usługi lub aktywności, przeprowadź weryfikację danych w ramach odbiornika intencji.
Usługi
Service
jest często używany do udostępniania funkcji innym aplikacjom. Każda klasa usługi musi mieć odpowiednią deklarację <service>
w pliku manifestu.
Domyślnie usługi nie są eksportowane i nie można ich wywoływać z żadnej innej aplikacji. Jeśli jednak do deklaracji usługi dodasz jakiekolwiek filtry intencji, zostanie ona wyeksportowana domyślnie. Najlepiej jest jawnie zadeklarować atrybut android:exported
, aby mieć pewność, że będzie on działał zgodnie z oczekiwaniami. Usługi można też chronić za pomocą atrybutu android:permission
. Aby inne aplikacje mogły uruchamiać, zatrzymywać lub łączyć się z usługą, muszą zadeklarować odpowiedni element <uses-permission>
w swoim pliku manifestu.
Uwaga: jeśli Twoja aplikacja jest kierowana na Androida 5.0 (poziom interfejsu API 21) lub nowszego, użyj **JobScheduler**
, aby wykonywać usługi w tle.
Usługa może chronić poszczególne wywołania IPC, które są do niej kierowane, za pomocą uprawnień. Aby to zrobić, przed wykonaniem implementacji wywołania należy wywołać funkcję checkCallingPermission()
. Zalecamy używanie deklaratywnych uprawnień w pliku manifestu, ponieważ są one mniej podatne na pomyłki.
Uwaga: nie myl uprawnień klienta z uprawnieniami serwera. Upewnij się, że wywoływana aplikacja ma odpowiednie uprawnienia i że przyznajesz te same uprawnienia aplikacji wywołującej.
Interfejsy Binder i Messenger
Używanie Binder
lub Messenger
jest preferowanym mechanizmem interfejsu IPC w stylu RPC na Androidzie. Zapewniają one dobrze zdefiniowane interfejsy, które umożliwiają wzajemne uwierzytelnianie punktów końcowych (w razie potrzeby).
Zalecamy projektowanie interfejsów aplikacji w taki sposób, aby nie wymagały sprawdzania uprawnień związanych z poszczególnymi interfejsami. Obiekty Binder
i Messenger
nie są deklarowane w manifeście aplikacji, więc nie możesz bezpośrednio do nich zastosować deklaratywnych uprawnień. Zwykle dziedziczą uprawnienia zadeklarowane w pliku manifestu aplikacji Service
lub Activity
, w którym są one zaimplementowane. Jeśli tworzysz interfejs, który wymaga uwierzytelniania lub kontroli dostępu, musisz wyraźnie dodać te elementy jako kod w interfejsie Binder
lub Messenger
.
Jeśli udostępniasz interfejs, który wymaga kontroli dostępu, użyj checkCallingPermission()
, aby sprawdzić, czy wywołujący ma wymagane uprawnienia. Jest to szczególnie ważne przed uzyskaniem dostępu do usługi w imieniu rozmówcy, ponieważ tożsamość aplikacji jest przekazywana do innych interfejsów.
Jeśli wywołujesz interfejs udostępniany przez usługę Service
, wywołanie bindService()
może się nie udać, jeśli nie masz uprawnień dostępu do danej usługi. Jeśli chcesz zezwolić procesowi zewnętrznemu na interakcję z aplikacją, ale nie ma on wymaganych uprawnień, możesz użyć metody clearCallingIdentity()
. Ta metoda wywołuje interfejs aplikacji tak, jakby to aplikacja, a nie zewnętrzny rozmówca, nawiązywała połączenie. Uprawnienia dzwoniącego możesz przywrócić później, korzystając z metody restoreCallingIdentity()
.
Więcej informacji o przekazywaniu IPC do usługi znajdziesz w artykule Usługi ograniczone.
Odbiorniki transmisji
BroadcastReceiver
obsługuje żądania asynchroniczne inicjowane przez Intent
.
Domyślnie odbiorniki są eksportowane i mogą być wywoływane przez dowolną inną aplikację.
Jeśli usługa BroadcastReceiver
jest przeznaczona do użytku przez inne aplikacje, możesz zastosować uprawnienia zabezpieczeń do odbiorników za pomocą elementu <receiver>
w pliku manifestu aplikacji. Zapobiega to wysyłaniu intencji do BroadcastReceiver
przez aplikacje bez odpowiednich uprawnień.
Bezpieczeństwo w przypadku kodu wczytywanego dynamicznie
Nie zalecamy wczytywania kodu spoza pliku APK aplikacji. Takie działanie znacznie zwiększa prawdopodobieństwo naruszenia bezpieczeństwa aplikacji z powodu wstrzyknięcia kodu lub manipulacji kodem. Wzrost złożoności zarządzania wersjami i testowania aplikacji może też uniemożliwić weryfikację zachowania aplikacji, dlatego może być zabroniony w niektórych środowiskach.
Jeśli Twoja aplikacja ładuje kod dynamicznie, pamiętaj, że kod ten działa z tymi samymi uprawnieniami zabezpieczeń co plik APK aplikacji. Użytkownik podejmuje decyzję o zainstalowaniu aplikacji na podstawie Twojej tożsamości i oczekuje, że w aplikacji będzie można uruchomić kod, w tym kod ładowany dynamicznie.
Wiele aplikacji próbuje wczytać kod z niebezpiecznych lokalizacji, takich jak sieci z niezaszyfrowanymi protokołami lub lokalizacje z możliwością zapisu dla wszystkich użytkowników, np. pamięć zewnętrzna. Te lokalizacje mogą umożliwiać komuś w sieci modyfikowanie treści w transmisji lub innej aplikacji na urządzeniu użytkownika w celu modyfikowania treści na urządzeniu. Z drugiej strony moduły zawarte bezpośrednio w pliku APK nie mogą być modyfikowane przez inne aplikacje. Dotyczy to zarówno natywnych bibliotek, jak i klas wczytywanych za pomocą DexClassLoader
.
Bezpieczeństwo w maszynie wirtualnej
Dalvik to środowisko wykonawcze maszyny wirtualnej (VM) Androida. Dalvik został stworzony specjalnie na potrzeby Androida, ale wiele problemów związanych z bezpiecznym kodem w innych maszynach wirtualnych dotyczy również Androida. Ogólnie nie musisz się martwić problemami z bezpieczeństwem związanymi z maszyną wirtualną. Aplikacja działa w bezpiecznym środowisku piaskownicy, więc inne procesy w systemie nie mają dostępu do kodu ani danych prywatnych.
Jeśli chcesz dowiedzieć się więcej o bezpieczeństwie maszyn wirtualnych, zapoznaj się z dostępną literaturą na ten temat. Oto 2 najpopularniejsze materiały:
Ten dokument skupia się na obszarach, które są specyficzne dla Androida lub różnią się od innych środowisk maszyn wirtualnych. Deweloperzy, którzy mają doświadczenie w programowaniu maszyn wirtualnych w innych środowiskach, mogą napotkać 2 ogólne problemy związane z tworzeniem aplikacji na Androida:
- Niektóre maszyny wirtualne, takie jak JVM czy środowisko wykonawcze .NET, działają jako granica zabezpieczeń, izolując kod od możliwości podstawowego systemu operacyjnego. Na Androidzie maszyna wirtualna Dalvik nie stanowi zabezpieczenia – piaskownica aplikacji jest implementowana na poziomie systemu operacyjnego, więc Dalvik może współpracować z kodem natywnym w tej samej aplikacji bez żadnych ograniczeń bezpieczeństwa.
- Ze względu na ograniczoną ilość miejsca na urządzeniach mobilnych deweloperzy często chcą tworzyć aplikacje modułowe i wykorzystywać dynamiczne wczytywanie klas. Podczas wykonywania tej czynności weź pod uwagę zarówno źródło, z którego pobierasz logikę aplikacji, jak i miejsce, w którym jest ona przechowywana lokalnie. Nie używaj dynamicznego ładowania klas z źródeł, które nie zostały zweryfikowane, takich jak niezabezpieczone źródła sieciowe lub pamięć zewnętrzna, ponieważ kod może zostać zmodyfikowany w taki sposób, aby zawierał złośliwe zachowanie.
Bezpieczeństwo w kodzie natywnym
Ogólnie zalecamy używanie pakietu Android SDK do tworzenia aplikacji zamiast korzystania z kodu natywnego z Android NDK. Aplikacje utworzone za pomocą kodu natywnego są bardziej złożone, mniej przenośne i częściej zawierają typowe błędy związane z uszkodzoną pamięcią, takie jak przepełnienie bufora.
Android jest tworzony na podstawie jądra Linuksa, a znajomość sprawdzonych metod dotyczących bezpieczeństwa w przypadku tworzenia aplikacji natywnych jest szczególnie przydatna, jeśli używasz kodu natywnego. W tym dokumencie nie omawiamy metod zabezpieczania systemu Linux, ale jednym z najpopularniejszych zasobów jest Secure Programming HOWTO – Creating Secure Software.
Ważną różnicą między Androidem a większością środowisk Linuxa jest piaskownica aplikacji. Na Androidzie wszystkie aplikacje działają w piaskownicy aplikacji, w tym te napisane w kodzie natywnym. Deweloperzy znający system Linux mogą sobie wyobrazić, że każda aplikacja ma niepowtarzalny identyfikator użytkownika (UID) z bardzo ograniczonymi uprawnieniami. Więcej informacji na ten temat znajdziesz w omówieniu zabezpieczeń Androida. Powinieneś też znać uprawnienia aplikacji, nawet jeśli używasz kodu natywnego.