Aplikacja, która działa od kilku lat, zwykle nie była projektowana z myślą o bezpieczeństwie. Powstawała szybko, potem dochodziły kolejne funkcje, zmieniali się wykonawcy, a nikt nie miał czasu, żeby spojrzeć na całość. Dopóki nic się nie dzieje, wszystko wydaje się w porządku. Problem w tym, że o luce zwykle dowiadujesz się dopiero wtedy, gdy ktoś ją wykorzysta.
Skutki są konkretne. Wyciek danych klientów oznacza obowiązek zgłoszenia naruszenia do UODO w ciągu 72 godzin, powiadomienie użytkowników i utratę zaufania. Przejęte klucze do usług zewnętrznych to rachunki za cudze zużycie. Nadużycie API potrafi w jeden weekend wygenerować tysiące złotych kosztów za SMS-y.
Ten tekst opisuje dziesięć problemów, które najczęściej znajdujemy, gdy audytujemy albo przejmujemy starsze aplikacje mobilne — jak je wykryć i jak je naprawić.
Jak sprawdzamy bezpieczeństwo aplikacji
Punktem odniesienia są standardy OWASP dla aplikacji mobilnych: lista najczęstszych zagrożeń OWASP Mobile Top 10 (zaktualizowana w 2024 r.) oraz szczegółowe wymagania MASVS wraz z przewodnikiem testowania MASTG. W praktyce audyt składa się z kilku części:
- analiza statyczna — przegląd kodu źródłowego i zbudowanej aplikacji (APK, IPA), także narzędziami takimi jak MobSF,
- dekompilacja — sprawdzenie, co zobaczy ktoś, kto pobierze aplikację ze sklepu i ją rozpakuje,
- analiza ruchu sieciowego — podgląd komunikacji aplikacji z serwerem przez proxy,
- testy API — sprawdzenie, czy serwer nie ufa aplikacji bardziej, niż powinien,
- przegląd konfiguracji i dostępów — konsole sklepów, Firebase, serwery, klucze podpisu.
Ostatni punkt jest często pomijany, a w starszych aplikacjach bywa najważniejszy.
1. Sekrety zapisane w kodzie aplikacji
Klucze do API płatności, map, wysyłki SMS czy chmury, wpisane bezpośrednio w kod aplikacji. To najczęstsze znalezisko w audytach — i jedno z najprostszych do wykorzystania. Każdy może pobrać aplikację ze sklepu, rozpakować ją darmowym narzędziem i w kilka minut przeszukać pod kątem kluczy. Obfuskacja kodu (np. R8 na Androidzie) utrudnia czytanie logiki, ale klucza nie ukryje.
Jak naprawić: potraktuj każdy klucz znaleziony w aplikacji jako ujawniony i go wymień. Klucze, które dają dostęp do danych albo kosztują pieniądze, przenieś na backend — aplikacja prosi serwer, a serwer wywołuje usługę zewnętrzną. Klucze, które muszą zostać w aplikacji (np. do map), ogranicz w konsoli dostawcy do identyfikatora Twojej aplikacji i jej certyfikatu podpisu.
2. Backend, który ufa aplikacji
Najpoważniejszy problem, bo dotyczy danych wszystkich użytkowników naraz. Typowy scenariusz: aplikacja pokazuje użytkownikowi tylko jego zamówienia, ale serwer zwraca dowolne zamówienie, jeśli w zapytaniu zmieni się numer z /orders/1023 na /orders/1024. Albo panel administratora jest ukryty w aplikacji, ale jego endpointy działają dla każdego zalogowanego użytkownika.
Aplikacja mobilna jest w rękach użytkownika — każde zapytanie, które wysyła, można podejrzeć i zmodyfikować. Ukrycie przycisku w interfejsie nie jest zabezpieczeniem.
Jak naprawić: każdy endpoint musi sprawdzać na serwerze, czy zalogowany użytkownik ma prawo do konkretnego zasobu. To praca po stronie backendu, którą warto połączyć z testami automatycznymi — tak, żeby nowy endpoint bez sprawdzenia uprawnień nie przeszedł przeglądu.
3. Dane i tokeny zapisane bez szyfrowania
Token logowania, dane osobowe albo historia zamówień zapisane w zwykłym pliku ustawień — SharedPreferences na Androidzie, UserDefaults na iOS, AsyncStorage w React Native. Na telefonie z rootem lub jailbreakiem, z kopii zapasowej albo przez złośliwą aplikację te dane da się odczytać.
Jak naprawić: tokeny i dane wrażliwe przechowuj w bezpiecznym magazynie systemu — Keystore na Androidzie, Keychain na iOS (we Flutterze np. flutter_secure_storage, w React Native react-native-keychain). Przy okazji sprawdź, czy aplikacja w ogóle musi przechowywać na telefonie wszystko, co przechowuje. Dane, których nie ma, nie wyciekną.
4. Sesje, które nigdy nie wygasają
Token wydany przy pierwszym logowaniu działa latami, a wylogowanie usuwa go tylko z telefonu — na serwerze wciąż jest ważny. Jeśli token wycieknie (z logów, kopii zapasowej, przechwyconego ruchu), daje dostęp do konta bez ograniczeń czasowych. Zmiana hasła często niczego nie zmienia.
Jak naprawić: krótko żyjący token dostępu i dłużej żyjący token odświeżania, unieważnianie sesji na serwerze przy wylogowaniu i zmianie hasła, a w aplikacjach z danymi wrażliwymi — możliwość wylogowania wszystkich urządzeń.
5. Niezabezpieczona komunikacja sieciowa
W nowych aplikacjach szyfrowany HTTPS jest standardem — Android od wersji 9 domyślnie blokuje nieszyfrowany ruch, a iOS wymaga HTTPS od lat. W starszych aplikacjach regularnie znajdujemy jednak wyjątki: zezwolenie na nieszyfrowany ruch dla całej aplikacji (usesCleartextTraffic, NSAllowsArbitraryLoads), dodane kiedyś „na chwilę” do testów, albo wyłączone sprawdzanie certyfikatu serwera, które miało działać tylko w wersji deweloperskiej.
Jak naprawić: usuń wyjątki, a jeśli któryś jest naprawdę potrzebny — ogranicz go do konkretnej domeny. Przypinanie certyfikatów (certificate pinning) daje dodatkową ochronę w aplikacjach finansowych i medycznych, ale wymaga procedury przy wymianie certyfikatu na serwerze — źle wdrożone potrafi z dnia na dzień odciąć wszystkich użytkowników.
6. Nieaktualne biblioteki ze znanymi lukami
Aplikacja ma kilkadziesiąt zależności, część nieaktualizowanych od lat, niektóre porzucone przez autorów. Luki w popularnych bibliotekach są publicznie opisane, więc ich wykorzystanie nie wymaga dużej wiedzy.
Jak naprawić: zrób listę zależności i sprawdź je pod kątem znanych podatności — npm audit, OWASP Dependency-Check, Snyk, a na bieżąco Dependabot lub Renovate, które same proponują aktualizacje. Porzucone biblioteki zastąp utrzymywanymi. Jeśli aktualizacja wymaga podniesienia całego frameworka, to osobny projekt — opisaliśmy go w tekście o aktualizacji starej aplikacji we Flutterze lub React Native.
7. Dane osobowe w logach i raportach awarii
Aplikacja wypisuje do logów pełne odpowiedzi serwera razem z tokenami, adresami i numerami telefonów. Raporty awarii w Crashlytics lub Sentry zawierają e-maile użytkowników, a zdarzenia w analityce — dane, które nie powinny tam trafić. To problem bezpieczeństwa i zarazem problem z RODO: dane osobowe trafiają do zewnętrznych usług, często bez podstawy i bez wpisu w rejestrze czynności przetwarzania.
Jak naprawić: wyłącz szczegółowe logowanie w wersjach produkcyjnych, filtruj dane wrażliwe przed wysłaniem raportów awarii i przejrzyj, jakie parametry wysyła analityka. W raportach awarii identyfikuj użytkownika wewnętrznym ID, a nie e-mailem.
8. Pozostałości z wersji deweloperskiej i otwarte drzwi
Do tej kategorii należy kilka podobnych problemów:
- ukryte menu testowe z przełącznikiem serwerów albo opcją „zaloguj jako dowolny użytkownik”, dostępne po kilkukrotnym dotknięciu logo,
- adresy serwerów testowych w kodzie produkcyjnym — a za nimi serwer testowy z kopią prawdziwych danych i słabszymi zabezpieczeniami,
- komponenty Androida dostępne dla innych aplikacji (exported) bez potrzeby i bez uprawnień,
- WebView, który udostępnia stronom internetowym natywne funkcje aplikacji albo otwiera dowolny adres przekazany z zewnątrz,
- deep linki, które bez sprawdzenia wykonują akcje, np. zmianę ustawień konta.
Jak naprawić: funkcje testowe usuń z wersji produkcyjnej na etapie budowania, a nie przez ukrycie w interfejsie. Przejrzyj manifest Androida i oznacz jako niedostępne wszystko, co nie musi być dostępne. Dane przekazywane przez deep linki i WebView traktuj jak niezaufane dane od użytkownika.
9. API bez ochrony przed nadużyciami
Endpointy logowania, rejestracji i wysyłki kodów SMS bez limitów prób. Skutki to nie tylko łamanie haseł metodą prób i błędów. Coraz częstszy jest SMS pumping: automat wysyła tysiące żądań kodu weryfikacyjnego na numery premium, a rachunek za SMS-y płaci właściciel aplikacji. W jeden weekend można w ten sposób stracić więcej, niż kosztuje roczne utrzymanie aplikacji.
Jak naprawić: limity prób na numer, adres IP i urządzenie, blokada wysyłki SMS do krajów, w których nie działasz, alerty przy nagłym wzroście kosztów. Dodatkowo warto sprawdzać, czy zapytania pochodzą z prawdziwej aplikacji — służą do tego Play Integrity API, App Attest na iOS albo Firebase App Check.
10. Dostępy, o których nikt nie pamięta
Byli programiści nadal mają dostęp do konsoli Google Play, App Store Connect, projektu Firebase i serwera. Hasło do bazy danych jest takie samo od pięciu lat i znają je wszyscy, którzy kiedykolwiek pracowali przy projekcie. Klucz podpisu aplikacji leży na prywatnym laptopie kogoś, z kim firma już nie współpracuje.
To problem, którego nie wykryje żadne narzędzie do analizy kodu, a w aplikacjach przejmowanych od innych wykonawców spotykamy go prawie zawsze.
Jak naprawić: zrób listę wszystkich kont i usług, usuń dostępy osób, które już nie pracują przy projekcie, zmień hasła i klucze, które mogły znać, i włącz uwierzytelnianie dwuskładnikowe. Listę kontrolną dostępów znajdziesz w tekście o przejęciu projektu po innym software housie, a o tym, co robić, gdy dostępy zostały u jednej osoby — w tekście o tym, co zrobić, gdy programista zniknął.
Od czego zacząć: priorytety
Problem Ryzyko Pilność Backend, który ufa aplikacji wyciek danych wszystkich użytkowników natychmiast Sekrety w kodzie aplikacji koszty, dostęp do danych w usługach zewnętrznych natychmiast — wymiana kluczy Dostępy byłych współpracowników przejęcie aplikacji, kont i danych natychmiast API bez limitów koszty SMS, przejmowanie kont w ciągu kilku tygodni Sesje bez wygasania, dane bez szyfrowania przejęcie pojedynczych kont w ciągu kilku tygodni Nieaktualne biblioteki zależnie od konkretnej luki plan aktualizacji Dane w logach, pozostałości debug RODO, ułatwienie ataku przy najbliższym wydaniuCzęść poprawek — wymiana kluczy, odebranie dostępów, limity na API — nie wymaga nowej wersji aplikacji w sklepie i można je wdrożyć w ciągu kilku dni. Zmiany w samej aplikacji trzeba zaplanować razem z wydaniem.
Co możesz sprawdzić samodzielnie
Bez wiedzy technicznej możesz w godzinę ustalić kilka rzeczy:
- kto ma dostęp do Google Play Console, App Store Connect, Firebase i serwera — i czy wszystkie te osoby nadal pracują przy projekcie,
- czy na tych kontach jest włączone uwierzytelnianie dwuskładnikowe,
- kto ma klucz podpisu aplikacji na Androida i czy aplikacja korzysta z Play App Signing,
- czy w ostatnich miesiącach rachunki za SMS-y, chmurę lub usługi zewnętrzne nie rosły bez wyraźnego powodu,
- kiedy ostatnio ktoś aktualizował biblioteki w aplikacji.
Jeśli na któreś pytanie odpowiedź brzmi „nie wiem” — to dobry punkt wyjścia do audytu.
Ile trwa audyt bezpieczeństwa
Przegląd typowej aplikacji mobilnej z backendem — analiza statyczna, dekompilacja, ruch sieciowy, testy API i przegląd dostępów — to zwykle od kilku dni do dwóch tygodni, zależnie od liczby funkcji i tego, czy audyt obejmuje backend. Wynikiem jest lista problemów z oceną ryzyka i rekomendacją naprawy, uporządkowana według pilności. Więcej o zakresie przeglądu kodu piszemy w tekście o audycie kodu źródłowego. Aplikacje finansowe mają dodatkowe wymagania regulacyjne — opisaliśmy je w tekście o aplikacjach dla fintechu.
Podsumowanie
Większość problemów z bezpieczeństwem w starszych aplikacjach mobilnych nie wynika z wyrafinowanych ataków, tylko z decyzji podjętych w pośpiechu: klucz wpisany w kod, serwer ufający aplikacji, token zapisany w zwykłym pliku, dostęp, którego nikt nie odebrał. Dobra wiadomość jest taka, że najgroźniejsze z nich — klucze, uprawnienia na serwerze i dostępy — można naprawić szybko, często bez wydawania nowej wersji aplikacji.
Jeśli nie wiesz, w jakim stanie jest Twoja aplikacja, zamów bezpłatną analizę aplikacji — wskażemy najpoważniejsze ryzyka. Pełny przegląd bezpieczeństwa prowadzimy w ramach audytów i konsultacji, a regularne aktualizacje i kontrolę dostępów — w ramach utrzymania aplikacji mobilnych.