Jak zweryfikować plik APK aplikacji na Androida

Spis treści

Część użytkowników smartfonów z Androidem na pokładzie żyje w głębokim przekonaniu, że instalowanie aplikacji spoza sklepu Google Play nie jest zbyt rozważnym posunięciem. Nie chodzi tutaj tylko o szeroko rozumiane alternatywne źródła aplikacji, np. serwis apkmirror ale również o Yalp/Aurora Store czy też repozytoria F-Droid. Zdaniem tych osób pobieranie aplikacji z zewnętrznych źródeł może skompromitować bezpieczeństwo systemu oraz zagrozić naszej prywatności. No jakby nie patrzeć wgrywanie czegokolwiek bez zastanowienia się co tak naprawdę instalujemy w systemie nie jest zbyt mądre. Dlaczego zatem nie weryfikujemy aplikacji obecnych w oficjalnym sklepie Google? Co chwila przecież można usłyszeć w mediach o syfie, który udało się co prawda z tego sklepu usunąć ale też jakaś większa liczba użytkowników taką aplikację zdążyła już zainstalować i używała jej przez dłuższy lub krótszy okres czasu. Rozumowanie na zasadzie, że aplikacje ze sklepu Google Play są bezpieczne, bo są obecne w sklepie Google Play, daje nam jedynie fałszywe poczucie bezpieczeństwa, które jest gorsze od całkowitego braku bezpieczeństwa, bo w tym drugim przypadku człowiek przynajmniej jest świadom czyhających na niego niebezpieczeństw i włącza myślenie. Jak zatem odróżnić aplikacje, które są w stanie nam wyrządzić krzywdę od tych, które tego nie mają na celu i czy faktycznie pobieranie aplikacji na Androida z innego źródła niż oficjalny sklep Google Play jest takie niebezpieczne?

Sklep Google Play, a alternatywne źródła aplikacji

Trzeba sobie zdać sprawę, że w sklepie Google Play znajduje się cała masa aplikacji, które nie należą do marki Google. Niby oczywista oczywistość ale już sam fakt, że w grę wchodzą podmioty zewnętrzne powinien nam dać do myślenia, wszak wraz ze wzrostem ilości aplikacji maleje zdolność weryfikacji tego co tak naprawdę do sklepu Google Play trafia. Człowiek zwyczajnie nie jest w stanie zweryfikować tych miliardów linijek kodu, a to otwiera drogę do implementowania w aplikacjach różnych niepożądanych funkcji, które mogą działać na naszą szkodę.

Sklep Google Play to jedynie medium dystrybucyjne umożliwiające zebranie w jednym miejscu rezultatów pracy całej masy różnych deweloperów aplikacji. Dzięki takiemu rozwiązaniu nie trzeba biegać po setkach stron w celu skompletowania zestawu appek, które chcemy mieć w systemie i później martwić się jeszcze o proces aktualizacji tak pozyskanego oprogramowania. Niemniej jednak, ta zaleta sklepu Google Play jest też i jego wadą zarazem, bo taki centralny punkt dystrybucyjny może stać się celem ataków. Dlatego nie powinno się ufać środkom dystrybucji i to bez znaczenia czy mamy do czynienia z oficjalnym sklepem Google Play czy innymi alternatywnymi źródłami aplikacji, np F-Droid czy serwis apkmirror. Jeśli cenimy sobie bezpieczeństwo i prywatność, to powinniśmy raczej starać się ufać konkretnym deweloperom, którzy wyrobili sobie reputację działając dla dobra jakiejś społeczności.

Schematy podpisywania plików APK

Aplikacje dostępne na Androida (w tym też te obecne w sklepie Google Play) są podpisane kluczami deweloperów konkretnych projektów na etapie budowania pakietu .apk . Dane zawarte w tym pliku są podpisane przy wykorzystaniu jednego z trzech schematów: JAR-signed APK (dla Androida <7) i APK Signature Scheme v2 (oraz v3 dla Androida 9 i późniejszych). Po pobraniu aplikacji ale przed jej instalacją w systemie, Android będzie próbował zweryfikować sygnaturę zanim zezwoli na kontynuację procesu instalacyjnego.

Problematyczny JAR-signed APK

W przypadku JAR-signed APK mamy do czynienia z podpisaniem zwykłego pliku JAR, gdzie sumy kontrolne są generowane osobno dla każdego z plików aplikacji. W przypadku tego schematu występują jednak problemy z bezpieczeństwem, jako że archiwum ZIP (którym jest plik .apk ) może zawierać dodatkowe bajty na początku, jak i również przed i między kolejnymi wpisami ZIP. Podpis, który jest generowany pod takim archiwum (i później weryfikowany) bierze pod uwagę jedynie same wpisy w ZIP i ignoruje zarazem wszystkie dodatkowe bity. Taki stan rzeczy umożliwia wstrzyknięcie pliku .dex w plik .apk bez wpływu na złożoną sygnaturę (pozostanie ona taka sama i, co gorsza, będzie poprawna). Podczas instalacji takiego zmienionego pakietu, Android zweryfikuje sygnaturę nie wykrywając przy tym zmian w ZIP ale Dalvik (maszyna wirtualna Androida) zinterpretuje ten plik .apk jako plik .dex i wykona jego kod (podatność Janus (CVE-2017-13156)).

Całą taką paczkę .apk trzeba też wypakować w celu weryfikacji jej zawartości, a to z kolei spowalnia cały proces instalacji i zwiększa wykorzystanie zasobów sprzętowych telefonu (pamięć RAM i procesor) i cierpi na tym też bateria.

APK Signature Scheme v2/v3

Z racji, że JAR-signed APK był dość nieefektywny i miał parę podatności, to by sobie z nimi poradzić opracowano APK Signature Scheme v2 i później też v3, w których to podpisywany jest cały plik .apk wraz z metadanymi ZIP i zmiana nawet jednego bitu w takiej paczce będzie skutkować błędem przy weryfikacji integralności danych, które są w niej zwarte. Jeśli chodzi zaś o różnice między v2 i v3 to w przypadku v3 zostały wprowadzone dodatkowe informacje, które są umieszczane w bloku podpisu (signing block).

apk-integrity-protection

Wsparcie dla wszystkich schematów podpisu

Wszystkie te trzy schematy podpisywania nie są kompatybilne wstecznie ze sobą. Jeśli dana aplikacja ma być instalowana w starszych i nowszych wersjach Androida, to trzeba ją podpisać przy pomocy każdego ze schematów. Później przy instalacji aplikacji, system podąża według poniższych instrukcji, by taki pakiet zweryfikować:

apk-validation-process

Jak zweryfikować plik APK

Dysponując wiedzą, że pliki .apk są podpisane, to drugorzędne znaczenie ma dla nas źródło, z którego je pobieramy. Niemniej jednak, wypadałoby zweryfikować kto się pod taką aplikacją podpisał oraz czy paczka (albo jej pliki) została w jakiś sposób zmieniona.

Jeśli pobieramy aplikacje ze sklepu Google Play, to tam zwykle jest wyszczególniony podmiot, od którego ta appka pochodzi. Niekiedy przy kontakcie z deweloperem jest nieco więcej informacji, np. strona projektu, czy adres. Warto też rzucić okiem na informacje o aplikacji, bo tam mogą być dodatkowe linki, np. do oficjalnego forum czy kodu źródłowego.

signal-app-google-play signal-app-google-play signal-app-google-play

Nie zawsze jednak wszystkie te informacje będą podane tak jak w przypadku tej powyższej aplikacji. Ja generalnie zalecam trzymanie się z dala od tych appek, których identyfikacja podmiotu jest problematyczna. Jeśli uda nam się zweryfikować podmiot, który stoi za aplikacją w sklepie Google Play, i mu ufamy, to możemy taką aplikację pobrać, zainstalować i tyle z naszej strony. Android w naszym telefonie przeprowadzi już sobie wszystkie niezbędne kroki pod kątem weryfikacji pliku .apk i albo zainstaluje aplikację jeśli weryfikacja przebiegnie z powodzeniem, albo nie pozwoli na jej instalację.

Jeśli zaś chcemy pobierać aplikacje z innych źródeł albo jedynie ciekawi nas jak taka weryfikacja aplikacji przebiega i jak ją przeprowadzić by zweryfikować integralność danych, to poniżej jest zamieszczona krótka instrukcja. Jako, że są w zasadzie dwa różne schematy podpisów, to trzeba zająć się nimi z osobna, bo taki plik .apk można instalować na starszych i nowszych Androidach i tylko ten fakt będzie wpływał na to, który schemat podpisu zostanie użyty podczas weryfikacji.

Za przykładową aplikację posłuży nam Signal. Plik tej appki można pobrać ze sklepu Google Play, z oficjalnej strony projektu, lub z każdego dowolnego miejsca, które nam wpadnie w łapki.

Weryfikacja schematu JAR-signed APK

Gdybyśmy się jedynie ograniczyli do pobrania pliku .apk z dowolnego źródła, do którego odesłałaby nas wyszukiwarka, to faktycznie moglibyśmy się obawiać instalacji tego pliku na urządzeniu z Androidem. Niemniej jednak, pobranie pliku to jedna sprawa, a jego weryfikacja, to osobna bajka.

W każdej paczce .apk znajduje się katalog META-INF/ , w nim zaś znajduje się kilka użytecznych dla nas plików:

  • META-INF/MANIFEST.MF - plik manifestu, który jest wykorzystywany do zdefiniowania rozszerzenia i danych powiązanych z pakietem.
  • META-INF/*.SF - plik z sygnaturami dla pliku JAR (w miejsce gwiazdki podstawia się konkretną nazwę).
  • META-INF/*.RSA - blokowy plik zawierający metadane certyfikatu, klucz publiczny oraz sygnaturę powiązaną z plikiem *.SF o tej samej nazwie. Plik ten jest nieczytelny dla człowieka.

Weryfikacja certyfikatu

Na stronie projektu Signal był podany odcisk palca SHA256 certyfikatu. Wygląda on tak:

signal-app-manual-download-certificate-hash

Po co nam ten odcisk? Przy jego pomocy jesteśmy w stanie zweryfikować podmiot, który ten certyfikat wystawił. Informacje o certyfikacie są dostępne w pliku META-INF/SIGNAL_S.RSA i możemy je uzyskać za sprawą narzędzia keytool :

$ unzip -p Signal-website-universal-release-*.apk META-INF/SIGNAL_S.RSA | keytool -printcert
Owner: CN=Whisper Systems, OU=Research and Development, O=Whisper Systems, L=Pittsburgh, ST=PA, C=US
Issuer: CN=Whisper Systems, OU=Research and Development, O=Whisper Systems, L=Pittsburgh, ST=PA, C=US
Serial number: 4bfbebba
Valid from: Tue May 25 17:24:42 CEST 2010 until: Tue May 16 17:24:42 CEST 2045
Certificate fingerprints:
         MD5:  D9:0D:B3:64:E3:2F:A3:A7:BD:A4:C2:90:FB:65:E3:10
         SHA1: 45:98:9D:C9:AD:87:28:C2:AA:9A:82:FA:55:50:3E:34:A8:87:93:74
         SHA256: 29:F3:4E:5F:27:F2:11:B4:24:BC:5B:F9:D6:71:62:C0:EA:FB:A2:DA:35:AF:35:C1:64:16:FC:44:62:76:BA:26
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 3

Mamy tutaj informacje na temat tego kto ten certyfikat wystawił, datę jego ważności oraz, to co nas interesuje najbardziej, czyli odcisk palca SHA256, który pasuje do tego ze strony projektu Signal. Wiemy zatem, że certyfikat należy do Whisper Systems (twórcy aplikacji Signal), no i tym samym udało nam się zweryfikować podmiot, który się pod tym plikiem .apk podpisał.

Przy weryfikacji certyfikatu można także posłużyć się poniższym poleceniem:

$ openssl pkcs7 -inform DER -in SIGNAL_S.RSA -noout -print_certs -text

Weryfikacja danych pliku APK

Teraz wypadałoby zweryfikować czy integralność danych w pliku .apk nie została naruszona w żaden sposób. By ręcznie zweryfikować paczkę .apk , trzeba posłużyć się plikiem META-INF/MANIFEST.MF . Poniżej znajduje się jego skrócona wersja:

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 3.5.1

Name: AndroidManifest.xml
SHA-256-Digest: S9ggdRXHREOfZVUyR3gVkkj7heljgNS3N2jG6j5AgwU=

Name: FingerprintProtocol.proto
SHA-256-Digest: WQ9R/MGV5m9Dye4Jtl3MiLRmX2RPoEPeinb3me7wnAU=

...

Name: res/xml/syncadapter.xml
SHA-256-Digest: Bh02ycHvc/OhwewyU6h1WDtEt+v7tNgKrauGEHs8azs=

Name: resources.arsc
SHA-256-Digest: DJGwbk9S1VPH6Cqkqa5A/8Sb3vYVPmbCZP/7G7EYkjs=

Poza nagłówkiem (trzema pierwszymi linijkami) mamy wpisy w postaci par Name/SHA-256-Digest . Jest ich dość sporo bo aż 3456. Plików w tej paczce zaś jest 3459, co daje różnice 3 plików. Zatem każdy plik za wyjątkiem tych powiązanych z podpisami ( MANIFEST.MF , SIGNAL_S.SF oraz SIGNAL_S.RSA ) ma wygenerowaną sumę kontrolą zakodowaną w base64 (SHA-256-Digest).

Dla przykładu weźmy sobie plik FingerprintProtocol.proto . By wyliczyć jego sumę kontrolną można posłużyć się poniższym poleceniem:

$ unzip -p Signal-website-universal-release-*.apk FingerprintProtocol.proto | \
    sha256sum | \
    xxd -r -p | \
    base64

WQ9R/MGV5m9Dye4Jtl3MiLRmX2RPoEPeinb3me7wnAU=

Wynikowy ciąg znaków pasuje do tego w pliku manifestu, zatem jego suma kontrolna się zgadza i plik nie został w żaden sposób zmieniony.

Podobnie Android weryfikuje każdy z plików przed instalacją aplikacji w systemie (przynajmniej jeśli chodzi o Andki <7). Gdyby zmienić któryś z tych plików, to jego suma kontrola byłaby inna i proces instalacji by się nie powiódł.

Oczywiście, gdyby polegać tylko na pliku META-INF/MANIFEST.MF , to naturalnie można by wygenerować nową sumę kontrolną i wstawić ją w stosowne miejsce, co czyniłoby całe zabezpieczenie bez sensu. Dlatego też z pomocą przychodzi nam plik META-INF/SIGNAL_S.SF , którego zawartość jest podobna do pliku META-INF/MANIFEST.MF . Poniżej jest skrócona zawartość META-INF/SIGNAL_S.SF :

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA-256-Digest-Manifest: 5GtdBgQlYLeNtJczTVbfG/rrh6YdoNxEZ2iq+BiRNA8=
X-Android-APK-Signed: 2, 3

Name: AndroidManifest.xml
SHA-256-Digest: PxbxNfqXevr9OY8iXnzwOdJf4POisDv7D/KBJ7p9yaE=

Name: FingerprintProtocol.proto
SHA-256-Digest: fWL5eppUj0x6UVdPqHusFkpDbXpq7P0sqchfublqEnA=

...

Name: res/xml/syncadapter.xml
SHA-256-Digest: or/WIJ91gQYMoGN/XzSmsSXP4oA8Y1Z8mI7HVyO/NgY=

Name: resources.arsc
SHA-256-Digest: 2oWTAFfBatMDkEJCDUocCNL5r0CTK5pl+8UFmIxLYvI=

Dwie rzeczy się rzucają w oczy od razu. Pierwszą z nich jest pole SHA-256-Digest-Manifest w nagłówku, którego wartość jest zakodowaną w base64 sumą kontrolną pliku META-INF/MANIFEST.MF :

$ unzip -p Signal-website-universal-release-*.apk META-INF/MANIFEST.MF | \
    sha256sum | \
    xxd -r -p | \
    base64

5GtdBgQlYLeNtJczTVbfG/rrh6YdoNxEZ2iq+BiRNA8=

Zatem plik manifestu nie został zmieniony w żaden sposób, bo jego suma kontrolna się zgadza.

Drugą rzeczą którą można zauważyć jest fakt, że wartości w SHA-256-Digest różnią się od tych obecnych w META-INF/MANIFEST.MF . Dzieje się tak ze względu na fakt, że w przypadku META-INF/SIGNAL_S.SF nie są hash'owane same pliki, a jedynie konkretne sekcje z pliku META-INF/MANIFEST.MF. Na sekcję zaś składa się linijka z Name: , SHA-256-Digest: oraz pusta linia (znak CRLF). Trochę to skomplikowane zatem najlepiej to zobrazować na przykładzie.

Sekcja pliku FingerprintProtocol.proto w META-INF/MANIFEST.MF ma poniższą postać:

manifest-section-hash

Jeśli taki wycinek tekstu podda się procesowi hash'owania, to otrzymamy:

$ cat data.txt | sha256sum | xxd -r -p | base64
fWL5eppUj0x6UVdPqHusFkpDbXpq7P0sqchfublqEnA=

I ten hash już pasuje do tego obecnego w pliku META-INF/SIGNAL_S.SF przy pozycji FingerprintProtocol.proto .

Oczywiście plik META-INF/SIGNAL_S.SF również można by poddać edycji i właśnie po to jest plik SIGNAL_S.RSA , który oprócz danych certyfikatu i klucza publicznego zawiera także sygnaturę złożoną pod plikiem META-INF/SIGNAL_S.SF w chwili tworzenia paczki .apk przez dewelopera. By taką sygnaturę złożyć potrzebny jest klucz prywatny, do którego zwykle ma dostęp dość ograniczone grono osób, przez co bardzo ciężko jest ten podpis podrobić.

Jeśli nie wiemy czy plik META-INF/SIGNAL_S.RSA zawiera jakąś sygnaturę, to zawsze możemy ten fakt sprawdzić przy pomocy poniższego polecenia:

$ openssl asn1parse -i -inform DER -in META-INF/SIGNAL_S.RSA
    0:d=0  hl=4 l=1023 cons: SEQUENCE
    4:d=1  hl=2 l=   9 prim:  OBJECT            :pkcs7-signedData
   15:d=1  hl=4 l=1008 cons:  cont [ 0 ]
   19:d=2  hl=4 l=1004 cons:   SEQUENCE
   23:d=3  hl=2 l=   1 prim:    INTEGER           :01
   26:d=3  hl=2 l=  15 cons:    SET
   28:d=4  hl=2 l=  13 cons:     SEQUENCE
   30:d=5  hl=2 l=   9 prim:      OBJECT            :sha256
   41:d=5  hl=2 l=   0 prim:      NULL
   43:d=3  hl=2 l=  11 cons:    SEQUENCE
   45:d=4  hl=2 l=   9 prim:     OBJECT            :pkcs7-data
   56:d=3  hl=4 l= 649 cons:    cont [ 0 ]
   60:d=4  hl=4 l= 645 cons:     SEQUENCE
   64:d=5  hl=4 l= 494 cons:      SEQUENCE
   68:d=6  hl=2 l=   3 cons:       cont [ 0 ]
   70:d=7  hl=2 l=   1 prim:        INTEGER           :02
   73:d=6  hl=2 l=   4 prim:       INTEGER           :4BFBEBBA
   79:d=6  hl=2 l=  13 cons:       SEQUENCE
   81:d=7  hl=2 l=   9 prim:        OBJECT            :sha1WithRSAEncryption
   92:d=7  hl=2 l=   0 prim:        NULL
   94:d=6  hl=3 l= 134 cons:       SEQUENCE
   97:d=7  hl=2 l=  11 cons:        SET
   99:d=8  hl=2 l=   9 cons:         SEQUENCE
  101:d=9  hl=2 l=   3 prim:          OBJECT            :countryName
  106:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :US
  110:d=7  hl=2 l=  11 cons:        SET
  112:d=8  hl=2 l=   9 cons:         SEQUENCE
  114:d=9  hl=2 l=   3 prim:          OBJECT            :stateOrProvinceName
  119:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :PA
  123:d=7  hl=2 l=  19 cons:        SET
  125:d=8  hl=2 l=  17 cons:         SEQUENCE
  127:d=9  hl=2 l=   3 prim:          OBJECT            :localityName
  132:d=9  hl=2 l=  10 prim:          PRINTABLESTRING   :Pittsburgh
  144:d=7  hl=2 l=  24 cons:        SET
  146:d=8  hl=2 l=  22 cons:         SEQUENCE
  148:d=9  hl=2 l=   3 prim:          OBJECT            :organizationName
  153:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  170:d=7  hl=2 l=  33 cons:        SET
  172:d=8  hl=2 l=  31 cons:         SEQUENCE
  174:d=9  hl=2 l=   3 prim:          OBJECT            :organizationalUnitName
  179:d=9  hl=2 l=  24 prim:          PRINTABLESTRING   :Research and Development
  205:d=7  hl=2 l=  24 cons:        SET
  207:d=8  hl=2 l=  22 cons:         SEQUENCE
  209:d=9  hl=2 l=   3 prim:          OBJECT            :commonName
  214:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  231:d=6  hl=2 l=  30 cons:       SEQUENCE
  233:d=7  hl=2 l=  13 prim:        UTCTIME           :100525152442Z
  248:d=7  hl=2 l=  13 prim:        UTCTIME           :450516152442Z
  263:d=6  hl=3 l= 134 cons:       SEQUENCE
  266:d=7  hl=2 l=  11 cons:        SET
  268:d=8  hl=2 l=   9 cons:         SEQUENCE
  270:d=9  hl=2 l=   3 prim:          OBJECT            :countryName
  275:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :US
  279:d=7  hl=2 l=  11 cons:        SET
  281:d=8  hl=2 l=   9 cons:         SEQUENCE
  283:d=9  hl=2 l=   3 prim:          OBJECT            :stateOrProvinceName
  288:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :PA
  292:d=7  hl=2 l=  19 cons:        SET
  294:d=8  hl=2 l=  17 cons:         SEQUENCE
  296:d=9  hl=2 l=   3 prim:          OBJECT            :localityName
  301:d=9  hl=2 l=  10 prim:          PRINTABLESTRING   :Pittsburgh
  313:d=7  hl=2 l=  24 cons:        SET
  315:d=8  hl=2 l=  22 cons:         SEQUENCE
  317:d=9  hl=2 l=   3 prim:          OBJECT            :organizationName
  322:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  339:d=7  hl=2 l=  33 cons:        SET
  341:d=8  hl=2 l=  31 cons:         SEQUENCE
  343:d=9  hl=2 l=   3 prim:          OBJECT            :organizationalUnitName
  348:d=9  hl=2 l=  24 prim:          PRINTABLESTRING   :Research and Development
  374:d=7  hl=2 l=  24 cons:        SET
  376:d=8  hl=2 l=  22 cons:         SEQUENCE
  378:d=9  hl=2 l=   3 prim:          OBJECT            :commonName
  383:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  400:d=6  hl=3 l= 159 cons:       SEQUENCE
  403:d=7  hl=2 l=  13 cons:        SEQUENCE
  405:d=8  hl=2 l=   9 prim:         OBJECT            :rsaEncryption
  416:d=8  hl=2 l=   0 prim:         NULL
  418:d=7  hl=3 l= 141 prim:        BIT STRING
  562:d=5  hl=2 l=  13 cons:      SEQUENCE
  564:d=6  hl=2 l=   9 prim:       OBJECT            :sha1WithRSAEncryption
  575:d=6  hl=2 l=   0 prim:       NULL
  577:d=5  hl=3 l= 129 prim:      BIT STRING
  709:d=3  hl=4 l= 314 cons:    SET
  713:d=4  hl=4 l= 310 cons:     SEQUENCE
  717:d=5  hl=2 l=   1 prim:      INTEGER           :01
  720:d=5  hl=3 l= 143 cons:      SEQUENCE
  723:d=6  hl=3 l= 134 cons:       SEQUENCE
  726:d=7  hl=2 l=  11 cons:        SET
  728:d=8  hl=2 l=   9 cons:         SEQUENCE
  730:d=9  hl=2 l=   3 prim:          OBJECT            :countryName
  735:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :US
  739:d=7  hl=2 l=  11 cons:        SET
  741:d=8  hl=2 l=   9 cons:         SEQUENCE
  743:d=9  hl=2 l=   3 prim:          OBJECT            :stateOrProvinceName
  748:d=9  hl=2 l=   2 prim:          PRINTABLESTRING   :PA
  752:d=7  hl=2 l=  19 cons:        SET
  754:d=8  hl=2 l=  17 cons:         SEQUENCE
  756:d=9  hl=2 l=   3 prim:          OBJECT            :localityName
  761:d=9  hl=2 l=  10 prim:          PRINTABLESTRING   :Pittsburgh
  773:d=7  hl=2 l=  24 cons:        SET
  775:d=8  hl=2 l=  22 cons:         SEQUENCE
  777:d=9  hl=2 l=   3 prim:          OBJECT            :organizationName
  782:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  799:d=7  hl=2 l=  33 cons:        SET
  801:d=8  hl=2 l=  31 cons:         SEQUENCE
  803:d=9  hl=2 l=   3 prim:          OBJECT            :organizationalUnitName
  808:d=9  hl=2 l=  24 prim:          PRINTABLESTRING   :Research and Development
  834:d=7  hl=2 l=  24 cons:        SET
  836:d=8  hl=2 l=  22 cons:         SEQUENCE
  838:d=9  hl=2 l=   3 prim:          OBJECT            :commonName
  843:d=9  hl=2 l=  15 prim:          PRINTABLESTRING   :Whisper Systems
  860:d=6  hl=2 l=   4 prim:       INTEGER           :4BFBEBBA
  866:d=5  hl=2 l=  13 cons:      SEQUENCE
  868:d=6  hl=2 l=   9 prim:       OBJECT            :sha256
  879:d=6  hl=2 l=   0 prim:       NULL
  881:d=5  hl=2 l=  13 cons:      SEQUENCE
  883:d=6  hl=2 l=   9 prim:       OBJECT            :rsaEncryption
  894:d=6  hl=2 l=   0 prim:       NULL
  896:d=5  hl=3 l= 128 prim:      OCTET STRING      [HEX DUMP]:
  AF5E96B4E77526640145A948CDE65B12945A0A036A52D27B630DBB6019422
  097231B6CA78EAB90DA6E3440C63C798D2E679742594E380768B63BA84D9F
  2021F50FBE63974FFE543C09A110319D144402D2C4A917604E6933C2DAB77
  2CFFB7E880A2995633A21F0E64810FF34DCA684BFDBC5D1DFEC1A75018150
  AF59404DD6B9

Ten długi ciąg na końcu to 256 bajtowa sygnatura złożona pod plikiem META-INF/SIGNAL_S.SF .

By teraz zweryfikować tę sygnaturę, możemy posłużyć się poniższy poleceniem:

$ openssl cms -verify -noverify -content META-INF/SIGNAL_S.SF -in META-INF/SIGNAL_S.RSA -inform der
...
Verification successful

Jak widać, weryfikacja została przeprowadzona z powodzeniem, a ufając jednocześnie certyfikatowi, można by zaufać zawartości paczki .apk (oczywiście, gdyby nie problemy natury bezpieczeństwa związane z tym schematem podpisu).

Jarsigner

Ręczna weryfikacja paczki .apk jest dość upierdliwa ale cały ten proces można zautomatyzować przy pomocy jarsigner . Wystarczy wpisać to poniższe polecenie w terminal:

$ jarsigner -verbose -verify Signal-website-universal-release-*.apk

sm     60312 Fri Nov 30 00:00:00 CET 1979 AndroidManifest.xml
sm       455 Thu Jan 01 01:00:00 CET 1970 FingerprintProtocol.proto
sm      3203 Thu Jan 01 01:00:00 CET 1970 LocalStorageProtocol.proto
...
sm    5616312 Fri Nov 30 00:00:00 CET 1979 resources.arsc
      409456 Fri Nov 30 00:00:00 CET 1979 META-INF/SIGNAL_S.SF
        1027 Fri Nov 30 00:00:00 CET 1979 META-INF/SIGNAL_S.RSA
s     409391 Fri Nov 30 00:00:00 CET 1979 META-INF/MANIFEST.MF

  s = signature was verified
  m = entry is listed in manifest
  k = at least one certificate was found in keystore
  i = at least one certificate was found in identity scope

- Signed by "CN=Whisper Systems, OU=Research and Development, O=Whisper Systems, L=Pittsburgh, ST=PA, C=US"
    Digest algorithm: SHA-256
    Signature algorithm: SHA256withRSA, 1024-bit key

jar verified.

Warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification
path to requested target
This jar contains entries whose signer certificate is self-signed.
This jar contains signatures that do not include a timestamp. Without a timestamp, users may not
be able to validate this jar after any of the signer certificates expire (as early as 2045-05-16).

Re-run with the -verbose and -certs options for more details.

The signer certificate will expire on 2045-05-16.

W wyjściu, które otrzymaliśmy, widoczne są wpisy poprzedzone sm . Każdy plik, który zostanie zweryfikowany poprawnie (jego suma kontrolna się zgadza) będzie miał znaczek s . Jeśli dodatkowo plik jest uwzględniony w META-INF/MANIFEST.MF , to będzie miał znaczek m . Wszystkie pliki w tej paczce za wyjątkiem trzech ostatnich są uwzględnione w pliku manifestu. Sumy kontrolne plików również się zgadzają, w tym też suma kontrolna samego pliku manifestu. Mamy również informację, że jar verified , czyli archiwum ZIP zostało zweryfikowane pomyślnie. W podsumowaniu mamy również informację, że certyfikat jest typu self-signed co może budzić pewne obawy ale tylko w przypadku gdybyśmy nie władali odciskiem palca tego certyfikatu (ten który został udostępniony na stronie Signal).

Weryfikacja APK Signature Scheme v2/v3

Z racji, że schemat JAR-signed APK jest obecnie odradzany, to wypadałoby zainteresować się nieco bardziej schematem APK Signature Scheme v2/v3 i zweryfikować przy jego pomocy aplikację. Nie znalazłem nigdzie ręcznej próby weryfikacji poszczególnych informacji, tak jak to było w przypadku JAR-signed APK, dlatego też ograniczymy się tutaj jedynie do automatu w postaci narzędzia apksigner.

Apksigner

Paczkę .apk można także zweryfikować przy pomocy apksigner . Niby w Debianie jest stosowny pakiet z tym narzędziem ale nie działa on za dobrze (zwraca błąd zsh: exec format error: apksigner ). Drugi problem wiąże się z posiadaniem starszej wersji apksigner , bo one najwyraźniej mają problemy z interpretacją nowszych schematów podpisów i nie są w stanie zweryfikować pliku .apk nawet jeśli ten jest podpisany prawidłowo. Dlatego upewnijmy się, że mamy najnowszą wersję tego narzędzia, np. za sprawą Android SDK Build Tools, które możemy dociągnąć przy pomocy Android Studio.

Zaletą apksigner jest niewątpliwie fakt, że jest on w stanie zweryfikować wszystkie schematy podpisów używane przy podpisywaniu aplikacji na Androida. W zasadzie, by zweryfikować plik .apk wystarczy wpisać w terminal to poniższe polecenie.

$ /media/Android/SDK/build-tools/29.0.2/apksigner verify --verbose --print-certs Signal-website-universal-release-*.apk
Verifies
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
Number of signers: 1
Signer #1 certificate DN: CN=Whisper Systems, OU=Research and Development, O=Whisper Systems, L=Pittsburgh, ST=PA, C=US
Signer #1 certificate SHA-256 digest: 29f34e5f27f211b424bc5bf9d67162c0eafba2da35af35c16416fc446276ba26
Signer #1 certificate SHA-1 digest: 45989dc9ad8728c2aa9a82fa55503e34a8879374
Signer #1 certificate MD5 digest: d90db364e32fa3a7bda4c290fb65e310
Signer #1 key algorithm: RSA
Signer #1 key size (bits): 1024
Signer #1 public key SHA-256 digest: 75336a3cc9edb64202cd77cd4caa6396a9b5fc3c78c58660313c7098ea248a55
Signer #1 public key SHA-1 digest: b46cbed18d6fbbe42045fdb93f5032c943d80266
Signer #1 public key MD5 digest: 0f9c33bbd45db0218c86ac378067538d
WARNING: META-INF/android.support.design_material.version not protected by signature. Unauthorized modifications to this JAR entry will not be detected. Delete or move the entry outside of META-INF/.
WARNING: META-INF/androidx.activity_activity.version not protected by signature. Unauthorized modifications to this JAR entry will not be detected. Delete or move the entry outside of META-INF/.
...

Z powyższych informacji widać, że pakiet został zweryfikowany ( Verifies ), zawiera wszystkie trzy schematy podpisu (każdy z nich z osobna również przechodzi weryfikacje poprawnie). Są też widoczne dane certyfikatu i klucza publicznego, w tym też jest hash SHA-256 certyfikatu, który pasuje do tego na stronie Signal. Jest też parę ostrzeżeń dotyczących plików w katalogu META-INF/ , które nie są chronione przez sygnatury ale te ostrzeżenia odnoszą się jedynie do schematu podpisu JAR-signed APK.

JAR-signed APK i starsze wersje Androida

Czy problemy ze schematem JAR-signed APK wpływają negatywnie na instalowanie aplikacji na starszych wersjach Androida? Jakby nie patrzeć to starsze Andki są w stanie zweryfikować plik .apk posługując się tylko schematem podpisu JAR-signed APK ale w dalszym ciągu aplikacje mogą być (i zwykle są) podpisane przy użyciu pozostałych schematów, tak jak widać to było na przykładzie aplikacji Signal.

Z racji, że w schemacie APK Signature Scheme v2/v3 jest weryfikowany cały plik .apk bit po bicie, to ta pierwsza sygnatura jest weryfikowana przez tą drugą (i trzecią), która gwarantuje integralność danych całej aplikacji. Jeśli korzystamy ze starszej wersji systemu, to wystarczy przy pomocy narzędzia apksigner ręcznie zweryfikować certyfikat i sygnaturę pliku .apk , który zamierzamy zainstalować w systemie. Oczywiście ten krok nie jest potrzebny w przypadku instalowania aplikacji z Google Play, bo ten sklep jest w stanie przeprowadzić za nas proces weryfikacji już na etapie przesyłania do niego aplikacji przez dewelopera.

Certyfikaty typu self-signed

Jeśli kogoś przeraża fakt, że certyfikat w aplikacji Signal był typu self-signed, czyli został podpisany przez ten sam podmiot, który wystawił certyfikat, to może odetchnąć z ulgą, bo taki jest świat Androida. Wszystkie aplikacje dostępne w sklepie Google Play mają tego typu podpisany przez siebie certyfikat i nie jest to niczym nagannym, czy niewłaściwym. Wszystko sprowadza się do zabezpieczenia klucza prywatnego, do którego dostęp ma zwykle jedna osoba i na tym kluczu opiera się cały łańcuch bezpieczeństwa. Jeśli klucz prywatny nie wpadnie w niepowołane łapki, to możemy być pewni co do aplikacji, która nim została podpisana.

Jeśli ktoś jest ciekaw jakie aplikacje w sklepie Google Play identyfikują się jakimś określonym certyfikatem, to można ten fakt również ustalić odwiedzając ten serwis i podając w nim hash SHA-1 certyfikatu.

Czy pobieranie aplikacji z Yalp/Aurora Store jest bezpieczne

By zainstalować appkę ze sklepu Google Play potrzebny nam jest stosowny klient, tj. aplikacja, która pobierze na flash smartfona plik .apk . Z reguły Android ma już na pokładzie aplikację sklepu Google Play i nie trzeba nic dodatkowo instalować, by móc pobierać za jej pomocą inne appki. Trzeba jednak wyraźnie zaznaczy tutaj, że aplikacja Google Play to jedynie klient sklepu, coś na wzór przeglądarki www. Istnieją inne klienty sklepu Google Play, np. otwartoźródłowe alternatywy takie jak Yalp store czy Aurora Store, które można wykorzystać do pobrania aplikacji z tego oficjalnego sklepu Google.

Zarówno Yalp Store jak i Aurora Store umożliwiają instalację tych samych aplikacji, które są dostępne w sklepie Google Play. Przez pojęcie "ta sama aplikacja" ma się oczywiście rozumieć ten sam plik .apk , który jest dostępny w sklepie Google Play (ten sam podpis cyfrowy i te same sumy kontrolne). Jeśli ufamy sklepowi Google Play i instalujemy z niego appki tylko dlatego, że w nim są, to taki sam poziom bezpieczeństwa nam daje Yalp/Aurora Store.

Plusem korzystania z któregoś z tych dwóch alternatywnych klientów sklepu Google Play jest fakt możliwości zrezygnowania zarówno z oficjalnego klienta sklepu Google, jak i pozbycie się całego Google Play Services, co może nam nieco poprawić prywatność przy korzystaniu ze smartfona z Androidem na pokładzie.

W każdym z tych trzech przypadków, pobierany jest plik .apk ilekroć tylko chcemy instalować czy aktualizować jakąś aplikację, którą mamy (bądź chcemy mieć) w systemie. Po pobraniu aplikacji, Android weryfikuje zarówno jej podpis jak i sumy kontrolne i jeśli wykryje problemy, to nie dopuści do instalacji czy też aktualizacji takiego pakietu.

Możemy zatem bez problemu instalować aplikacje za sprawą Yalp/Aurora Store i nie powinniśmy się obawiać, że te klienty wgrają nam same z siebie jakiś syf. A nawet jeśli już komuś takie pomysły chodzą po głowie, to na obronę Yalp/Aurora Store można rzucić argument, że ich kod jest otwarty, w przeciwieństwie do kodu aplikacji sklepu Google Play.

Czy pobieranie aplikacji z F-Droid jest bezpieczne

W przypadku F-Droid mamy do czynienia z repozytoriami (coś na wzór tych linux'owych), z których można pobierać aplikacje. Generalnie to repozytoria można podzielić na te oficjalne, którymi zarządza F-Droid, na oficjalne innych projektów (np. microg) i nieoficjalne, do których powinno się podchodzić z rozwagą. Lista znanych repozytoriów F-Droid jest dostępna tutaj.

Aplikacje znajdujące się w głównym repozytorium F-Droid można z kolei podzielić na dwa typy. Pierwszym są aplikacje budowane z konkretnych źródeł, a wynikowe pliki .apk są podpisywane przez klucz repozytorium. W efekcie wiele aplikacji jest podpisanych jednym kluczem. Drugim typem są aplikacje budowane i podpisywane przez swoich deweloperów. Zwykle będziemy mieć do czynienia z tym pierwszym typem.

By aplikacja mogła zagościć w oficjalnym repozytorium F-Droid musi być ona z gatunku FreeSoftware. W przypadku sklepu Google Play ogromna większość aplikacji ma zamknięty kod i do tego jest obłożona reklamami. Widać zatem fundamentalne różnice w podejściu do bezpieczeństwa i prywatności użytkowników między tymi dwoma źródłami aplikacji i niezmiernie ciężko jest do oficjalnego repozytorium F-Droid przemycić trefne aplikacje w przeciwieństwie do sklepu Google Play. Dodatkowo, Google usuwa niewygodne aplikacje, dla przykładu Blokada, Adaway czy sam F-Droid.

Czy pobieranie aplikacji z apkmirror.com jest bezpieczne

Jednym z popularniejszych serwisów z plikami .apk , które można pobrać i manualnie zainstalować w Androidzie jest apkmirror. Użytkownicy cenią go sobie ze względu na te trzy poniższe cechy, których im sklep Google Play nie może zapewnić.

Pierwszym atutem apkmirror jest możliwość zainstalowania starszych wersji aplikacji, które w sklepie Google Play przestały być dostępne z racji aktualizacji pakietu. W efekcie jeśli nowsza wersją aplikacji zaczyna nam zjadać z niewiadomego powodu baterię (albo zachowywać się w inny nieprzewidziany i do końca poprawny sposób), to przy pomocy apkmirror możemy z tym problemem sobie poradzić instalując starszą wersję aplikacji. Druga sprawa to nowsze wersje aplikacji, do których można mieć dostęp w przypadku, gdy sklep Google Play ociąga się z jakiegoś powodu z ich publikacją. No i ostatnia rzecz, to fakt posiadania przez apkmirror aplikacji nieobecnych w oficjalnym sklepie Google.

Te wszystkie powyższe ficzery serwisu apkmirror sprawiają, że użytkownik może być skłonny zainstalować z niego plik .apk ale czy możemy być pewni co do tych plików, które tam na stronie się znajdują? Czy instalując taki plik na pewno nie wgramy sobie syfu do systemu? Odpowiedź może być tylko jedna -- sprawdźmy co tam znajdziemy.

Weźmy sobie ponownie aplikację Signal na celownik, z racji, że znamy odcisk palca jej certyfikatu. Pobieramy dowolny plik .apk z listy, która się nam pojawi po kliknięciu w link. Zanim jednak faktycznie pobierzemy plik, to na stronie mamy informację, że Verified safe to install (read more) , czyli, że aplikacja została zweryfikowana i jest bezpieczna do zainstalowania. Jeśli klikniemy w ten link, to zobaczymy to poniższe okienko:

signal-app-verify-apkmirror

Mamy tutaj widoczne hash'e, z których jeden zestaw zawiera odciski palca certyfikatu (niby ten sam co na stronie projektu Signal). Lepiej jednak nie wierzyć co tam na podejrzanych stronach wypisują, więc lepiej podejść do tej informacji ostrożnie, przecie każdy mógłby przepisać ten kawałek tekstu z dowolnego miejsca i wstawić na stronę. Drugi zestaw hash'y odnosi się do sumy kontrolnej samego pliku, który figuruje w serwisie apkmirror.

Pobierzmy zatem już plik i sprawdźmy czy suma kontrolna pliku się zgadza:

$  sha256sum org.thoughtcrime.securesms_4.51.6-5792_minAPI19(arm64-v8a)(nodpi)_apkmirror.com.apk
b0c89cc55915839ab957f9ef7d9077fbde4550013acd2b713e51ad7e534ada2c  org.thoughtcrime.securesms_4.51.6-5792_minAPI19(arm64-v8a)(nodpi)_apkmirror.com.apk

Jak widać, suma kontrolna pliku pasuje do tego co zostało napisane na stronie. Zatem pobraliśmy plik w formie niezmienionej z serwera apkmirror. Sprawdźmy teraz odcisk palca certyfikatu, który siedzi w pliku .apk :

$ /media/Android/SDK/build-tools/29.0.2/apksigner verify --verbose --print-certs org.thoughtcrime.securesms_4.51.6-5792_minAPI19(arm64-v8a)(nodpi)_apkmirror.com.apk
Verifies
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
Number of signers: 1
Signer #1 certificate DN: CN=Whisper Systems, OU=Research and Development, O=Whisper Systems, L=Pittsburgh, ST=PA, C=US
Signer #1 certificate SHA-256 digest: 29f34e5f27f211b424bc5bf9d67162c0eafba2da35af35c16416fc446276ba26
Signer #1 certificate SHA-1 digest: 45989dc9ad8728c2aa9a82fa55503e34a8879374
Signer #1 certificate MD5 digest: d90db364e32fa3a7bda4c290fb65e310
Signer #1 key algorithm: RSA
Signer #1 key size (bits): 1024
Signer #1 public key SHA-256 digest: 75336a3cc9edb64202cd77cd4caa6396a9b5fc3c78c58660313c7098ea248a55
Signer #1 public key SHA-1 digest: b46cbed18d6fbbe42045fdb93f5032c943d80266
Signer #1 public key MD5 digest: 0f9c33bbd45db0218c86ac378067538d
WARNING: META-INF/android.support.design_material.version not protected by signature. Unauthorized modifications to this JAR entry will not be detected. Delete or move the entry outside of META-INF/.
WARNING: META-INF/androidx.activity_activity.version not protected by signature. Unauthorized modifications to this JAR entry will not be detected. Delete or move the entry outside of META-INF/.
...

Jak widać, odcisk palca certyfikatu również się zgadza. Mamy też informację, że dane w pliku .apk nie zostały zmienione w żaden sposób, zatem mamy do czynienia z plikiem, który wyszedł spod młotka tego samego dewelopera. Nic zatem nie stoi na przeszkodzie by taki plik .apk zainstalować w systemie.

Aktualizacja aplikacji z niezaufanego źródła

Z racji, że wszystkie aplikacje na Androida (nie tylko te w sklepie Google Play) muszą być podpisane by zostać zainstalowane w systemie, to nie ma zbytnio dla nas znaczenia skąd taką aplikację pobierzemy, o ile weryfikacja pliku .apk zostanie zakończona powodzeniem.

W przypadku aktualizacji pakietu nie musimy ręcznie go weryfikować. Jeśli certyfikaty będą się różnić albo dane w pakiecie zostaną w jakiś sposób zmienione, to Android nie dopuści do aktualizacji takiego pakietu. Mając zainstalowaną w systemie appkę Signal, mogę spróbować wgrać na telefon plik pobrany z serwisu apkmirror i zobaczyć co się w takim przypadku stanie:

signal-apk-install-apkmirror signal-apk-install-apkmirror signal-apk-install-apkmirror

Jak widać po fotkach wyżej, paczka .apk bez problemu się zainstalowała w systemie dokładnie w taki sam sposób jakbym aktualizował ją via sklep Google Play (tylko w tym przypadku trzeba zaznaczyć w opcjach Androida możliwość instalowania aplikacji z nieznanych źródeł). Z racji, że proces aktualizacji aplikacji przebiegł pomyślnie, to weryfikacja certyfikatu i danych w pakiecie .apk również zakończyła się powodzeniem. Podobnie sprawa będzie wyglądać przy korzystaniu z Yalp Store czy Aurora Store, bo dla Androida nie ma znaczenia skąd i w jaki sposób pobierany jest plik, tylko liczą się sumy kontrolne i podpisy cyfrowe.

Warto tutaj wspomnieć, że F-Droid buduje aplikacje bezpośrednio z ich źródeł, przez co wynikowe pliki .apk nie są tymi samymi, które wypuścił deweloper aplikacji. Ponadto, pliki .apk w repozytorium F-Droid są podpisane kluczem repozytorium, a nie kluczem dewelopera, zatem odcisk palca certyfikatu pliku .apk będzie się różnił. Dlatego też możemy zapomnieć o krosowym aktualizowaniu aplikacji (F-Droid <-> Google Play).

By zaktualizować aplikację, która została podpisana innym kluczem niż ta obecna już w systemie naszego smartfona, trzeba ją pierw odinstalować (ewentualnie wgrać dodatek do Xposed). Chodzi tutaj generalnie o sprawy związane z prywatnością i bezpieczeństwem. Każda aplikacja w systemie ma przypisany inny ID użytkownika i tylko ta aplikacja ma dostęp do swoich plików. Gdyby było możliwie zaktualizowanie aplikacji, która identyfikuje się innym certyfikatem, to potencjalnie inna appka mogłaby uzyskać dostęp do danych tej konkretnej aplikacji (w tym też przejąc jej uprawnienia, np. do mikrofonu czy kamery), a jak wiadomo, usunięcie aplikacji z systemu czyści jej wszystkie prywatne pliki.

Instalacja aplikacji z niezaufanego źródła

O ile proces aktualizacji jest bardzo prosty i automatyczny pod względem weryfikacji wszystkich tych niezbędnych z punktu widzenia bezpieczeństwa rzeczy, to w przypadku, gdy zamierzamy instalować po raz pierwszy aplikację z innego źródła niż oficjalny sklep Google Play, to niestety trzeba manualnie (najlepiej przy pomocy apksigner ) zweryfikować plik, który zamierzamy zainstalować w naszym smartfonie z Androidem. Dopiero po pomyślnym zweryfikowaniu pliku .apk możemy go zainstalować bez obaw.

A jeśli aplikacja nie publikuje odcisku palca certyfikatu

W przypadku aplikacji Signal wszystko było podane praktycznie na tacy i nie wymagało od użytkownika większego wysiłku intelektualnego, by taki plik .apk zweryfikować. Niemniej jednak, ogromna większość aplikacji niebędących spod znaku FreeSoftware nie posiada nawet swojej strony www. Jedyne informacje o takiej appcę są dostępne w sklepie Google Play. Przykładem mogą być aplikacje banków, czy też innych instytucji finansowych. Na stronie takiego banku jest z reguły tylko wzmianka o tym, że mają oni aplikację oraz jest podany też link do sklepu Google Play skąd ją można pobrać. Jak w takim przypadku zweryfikować plik .apk , który chcielibyśmy zainstalować w swoim Androidzie pozbawionym choćby tego całego Google Play Services?

Jedyną opcją w takiej sytuacji jest pozyskanie wiarygodnego pliku .apk ze sklepu Google Play i poddanie go weryfikacji przy pomocy narzędzia apksigner . Jeśli nie ufamy Yalp/Aurora Store, to taki plik można też wydobyć z Androida po zainstalowaniu aplikacji przy pomocy tego oficjalnego klienta sklepu Google Play. By wydobyć plik .apk ze smartfona, wystarczy zainstalować sobie jakieś menadżer plików, np. Amaze, który posiada wbudowany menadżer aplikacji. W tym menadżerze wystarczy wykonać backup aplikacji i stosowny plik .apk zostanie zapisany np. na kacie SD.

amaze-file-manager-app-backup

Tak wykonany backup aplikacji to nic innego jak tylko skopiowanie pliku .apk z jednego miejsca w drugie. Można ten fakt potwierdzić porównując hash pliku zainstalowanej aplikacji z plikiem backupu:

$ sha256sum Signal_org.thoughtcrime.securesms*
b0c89cc55915839ab957f9ef7d9077fbde4550013acd2b713e51ad7e534ada2c  Signal_org.thoughtcrime.securesms-backup.apk
b0c89cc55915839ab957f9ef7d9077fbde4550013acd2b713e51ad7e534ada2c  Signal_org.thoughtcrime.securesms.apk

Jak widać sumy kontrolne obu plików się zgadzają. Zakładając, że nie dysponujemy żadnym hash'em certyfikatu, to mając wiarygodny plik można go poddać weryfikacji w apksigner i w ten sposób uzyskać dane o certyfikacie, które mogą posłużyć później do weryfikacji certyfikatu plików .apk aplikacji pobieranych z innych niezaufanych źródeł.

Podsumowanie

Jak widać na przedstawionym przykładzie aplikacji Signal, nie ma znaczenia skąd pobieramy pliki .apk do momentu, gdy jesteśmy w stanie zweryfikować/potwierdzić certyfikat i integralność danych w pakiecie. W przeszłości mechanizm weryfikacji plików .apk w Androidzie nie gwarantował tego co powinien (choć nie była to wina samego mechanizmu) ale ten fakt uległ zmianie po zastąpieniu schematu podpisu JAR-signed APK przez APK Signature Scheme v2/v3.

Nie ma co zatem popadać w paranoję i zakładać, że tylko aplikacje pobrane z oficjalnego sklepu Google Play są wiarygodne i bezpieczne, bo gdy instalujemy aplikację w Androidzie bez jej weryfikacji, to zaciągamy dług wobec naszej prywatności. Z każdą kolejną bezrefleksyjnie zainstalowaną appką z tego sklepu coraz bardziej zapominamy jak ważny jest proces weryfikacji pochodzenia oprogramowania, od którego nasze życie coraz bardziej zaczyna zależeć. Jeśli dalej będziemy szli tą drogą, to prędzej czy później ten dług trzeba będzie spłacić i nie będziemy z tego faktu zbytnio zadowoleni.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.