Streng geheim
Wie man die Verschlüsselungsfunktionen von Android in eigene Apps einbaut
Android verschlüsselt zwar alle Nutzerdaten, aber App-Entwickler sollten die Grenzen dieser Verschlüsselung kennen und besonders wichtige Informationen zusätzlich schützen. Zu diesem Zweck bietet Android einfach anzuwendende APIs für biometrische Authentifizierung, Verschlüsselung und Signaturen.
Smartphones dienen als zweiter Faktor für die Anmeldung bei Banken und Webdiensten, speichern Gesundheitsdaten, intime Nachrichten, Standortverläufe und vieles mehr. Es ist wichtig, diese Daten gut zu schützen. Android beherrscht seit der 2009 veröffentlichten Version 4 eine Verschlüsselung des gesamten Flash-Speichers, seit Android 6 müssen die Hersteller alle Smartphones und Tablets ab Werk verschlüsselt ausliefern. Die Verschlüsselung erfolgt auf Sektorebene direkt im Dateisystemtreiber. Das geht vollautomatisch und sehr schnell, zumal häufig spezielle Hardware zur Beschleunigung genutzt wird [1].
Der eigentliche Schlüssel, der Masterkey, liegt in einem speziellen Disk-Bereich, welcher wiederum mit dem vom Benutzer vergebenen Passcode verschlüsselt ist. Sobald er seinen Passcode beim Entsperren des Geräts eingibt, entschlüsselt Android den Masterkey und kann damit bis zum Herunterfahren des Geräts auf sämtliche Dateien zugreifen. Zum Zurücksetzen des Geräts genügt es, den Masterkey zu löschen, statt mühsam den gesamten Speicherplatz Sektor für Sektor zu überschreiben. Bestimmte Informationen wie Verzeichnislayouts oder Dateigrößen sind von der diskbasierten Verschlüsselung ausgenommen. Damit Angreifer auch aus solchen Daten keine Rückschlüsse ziehen können, ist ab Android 9 zusätzlich eine separate Metadaten-Verschlüsselung möglich.
Diese Vollverschlüsselung (Full Disc Encryption, FDE) funktioniert sehr gut, hat aber auch Nachteile. Da die Nutzer ihr Mobilgerät nur sehr selten ganz ausschalten, liegen alle Daten die meiste Zeit komplett entschlüsselt vor. Mehrere Benutzerbereiche oder Unternehmensprofile lassen sich nicht voreinander schützen. Außerdem: Schaltet sich das Handy beispielsweise wegen eines Firmware-Updates oder eines leeren Akkus ab, ist es nach dem Einschalten nicht in der Lage, Anrufe entgegenzunehmen oder Wecker klingeln zu lassen. Auch Verbindungen mit dem WLAN und ein personalisierter Sperrbildschirm sind erst möglich, wenn der Passcode eingegeben wurde.
Dateibasierte Verschlüsselung
Android 7 und höher können daher eine dateibasierte Verschlüsselung (File Based Encryption, FBE) verwenden, wie sie auch Apple in iOS nutzt. Ab Android 10 ist FBE sogar Pflicht. Seit Android 9 kommt FBE auch für SD-Karten zum Einsatz, die per „Adoption“ eingebunden sind; vorher waren sie per FDE verschlüsselt.
Bei FBE gibt es einen Device Encrypted Storage, der schon beim Booten zur Verfügung steht und wichtige Systemdaten enthält. Zusätzlich bekommt jeder Benutzer einen eigenen Bereich namens Credential Encrypted Storage, der wie bisher die Apps und ihre Daten enthält und mit seinem Passcode verschlüsselt wird. Es geht also nicht darum, jede Datei mit einem anderen Kennwort zu verschlüsseln – die dafür nötige Schlüsselverwaltung wäre viel zu komplex. Vielmehr sollen bestimmte Datenbereiche ohne Passworteingabe schon beim Booten verfügbar sein. Und das, ohne die Disk in mehrere Partitionen aufteilen zu müssen.
Wichtig: Davor, dass bösartige Apps die Daten anderer Apps auslesen, schützt weiterhin Androids Sandbox-Mechanismus mit separaten User-IDs pro App. Er lässt sich auf einem gerooteten Gerät oder bei Zugriff auf das Backup leicht aushebeln. Als weiteres Risiko sind alle Apps ohne zusätzlichen Schutz offen einsehbar, wenn man sein Handy kurz aus der Hand gibt. Zudem kommt es immer mal wieder vor, dass böswillige Apps im Play Store landen, die Sicherheitslücken auf den Geräten ausnutzen, um Daten abzugreifen.
Android kümmert sich also nur um die Sicherheit des Flash-Speichers gegenüber externen Angreifern – aber der zusätzliche Schutz besonders geheimer Daten obliegt weiterhin dem App-Entwickler. Dabei gibt es verschiedene Aspekte, von denen wir die wichtigsten am Beispiel einer einfachen Tagebuch-App beleuchten. In der bewusst einfach gehaltenen App kann man pro Tag einen Text schreiben und seine Stimmung angeben. Damit die in dieser App erfassten intimen Gedanken und Aufzeichnungen nicht ungeschützt einsehbar sind, soll sie bei jedem Aufruf biometrisch entsperrt werden.
Vermessung
Biometrische Authentifizierung wird von Android inzwischen gut und sicher unterstützt. Die Zeiten, in denen manche Hersteller einfach Fotos von Fingerabdrücken auf dem Gerät gespeichert haben, sind zum Glück schon lange vorbei. Je nach Hardware kann die Authentifizierung über Fingerabdruck, Gesicht, Stimme, Iris oder ähnliches erfolgen. Das Android-Open-Source-Projekt definiert klare Anforderungen an die biometrischen Sensoren und teilt sie in zwei Sicherheitsklassen ein. Sensoren der höheren Sicherheitsklasse BIOMETRIC_STRONG müssen besser gegen Fälschungen geschützt sein als die der Klasse BIOMETRIC_WEAK, außerdem müssen die biometrischen Merkmale kryptografisch geschützt abgespeichert werden. Eine Erkennung von Lebenszeichen wie Augenbewegungen oder der Temperatur des Fingers wird stark empfohlen, ist für Gerätehersteller aber keine Pflicht. Die fälschliche Akzeptanz eines zufälligen (fremden) Fingers oder Gesichts darf bei beiden Varianten 1:50.000 nicht überschreiten.
Ob Ihr Gerät eine starke oder schwache Implementierung hat, merken Sie daran, wie oft der Passcode („Zur Verbesserung der Sicherheit ...“) abgefragt wird. Bei schwacher Authentifizierung muss dies mindestens einmal in 24 Stunden erfolgen, bei der höheren Sicherheitsstufe ist die Abfrage nur alle drei Tage erforderlich. Die biometrische Authentifizierung ist natürlich niemals sicherer als der Passcode des Geräts. Kennt oder errät man den, kann man Fingerabdrücke ändern/hinzufügen, ein anderes Gesicht anlernen oder den Schutz gleich ganz ausschalten.
Zur Abfrage gibt es die Bibliothek androidx.biometric, die man wie gewohnt in build.gradle einbindet. Achtung, die im Folgenden verwendeten Klassen gibt es mit gleichem Namen auch im Package android.hardware.biometrics, es kommt also auf den richtigen Import an. Mittels
BiometricManager.from(context). canAuthenticate(BiometricManager. Authenticators.BIOMETRIC_WEAK)
prüft man, ob eine biometrische Authentifizierung für das Gerät vom Nutzer konfiguriert und aktiviert wurde. Im Erfolgsfall liefert die Funktion BIOMETRIC_SUCCESS zurück. Ist dies der Fall, kann die Authentifizierung mittels BiometricPrompt losgehen (siehe Listing).
Mit den eigentlichen biometrischen Merkmalen, also Fingerabdruck, Stimme oder Gesicht kommen Entwickler aus Sicherheitsgründen gar nicht in Berührung, das übernimmt das System. Damit der Nutzer die App auch entsperren kann, wenn sein Finger verletzt oder das Gesicht von einer Maske verdeckt ist, sollte die App als zweiten Authenticator immer auch den Passcode des Geräts (DEVICE_CREDENTIAL) erlauben.
Die App sollte den Prompt beim Start anzeigen und jedes Mal, wenn der Anwender sie in den Vordergrund holt. Dazu muss man einen Listener für ActivityLifecycleCallbacks registrieren. Das geschieht am besten in einer eigenen abgeleiteten class App extends Application implements Application.ActivityLifecycleCallbacks. Diese registriert man im Manifest mittels <application android:name=".App" ....
In den Callback-Methoden onActivityStarted und onActivityStopped zählt man mit, wie viele Aktivitäten gestartet und gestoppt wurden. Ist die Anzahl 0, wurde die App gerade gestartet oder in den Vordergrund geholt und der biometrische Prompt sollte angezeigt werden.
Die App-Klasse ist auch der richtige Ort, um von überall schnell ein Context-Objekt zu erhalten, etwa zum Laden von Ressourcen mit Context context = App.instance.getApplicationContext().
Zugesperrt
Die App soll die Tagebucheinträge als verschlüsselte Textdateien auf dem Gerät speichern. So sind sie sicher, selbst wenn Angreifer irgendwie an das Datenverzeichnis der App gelangen, beispielsweise mit einem Dateimanager, aufgrund eines Android-Bugs, auf einem gerooteten Gerät oder auf einem alten Gerät ohne FDE/FBE.
Wenn es um Verschlüsselung geht, gilt als oberste Regel: Erfinde oder implementiere niemals einen Verschlüsselungsalgorithmus selbst. Auch wenn alles funktioniert, macht man viel zu leicht irgendwo einen Fehler und die Lösung ist angreifbar. Alle ernst zu nehmenden kryptografischen Implementierungen sind Open Source und von Experten auf Schwachstellen abgeklopft. Außerdem nutzen sie die vorhandene Sicherheits-Hardware auf dem Gerät, dazu gleich mehr.
In der Bibliothek androidx.security.crypto hat das Android-Team die wichtigsten Methoden zum Verschlüsseln von Dateien und SharedPreferences, sowie das Verwalten der zugehörigen Schlüssel zusammengefasst. Sie erlaubt eine Implementierung der Dateiverschlüsselung in nur wenigen Schritten.
Zunächst besorgt man sich einen MasterKey für die symmetrische AES-256-Verschlüsselung:
MasterKey.Builder builder = new MasterKey.Builder(App.appContext(), "diary").setKeyScheme( MasterKey.KeyScheme.AES256_GCM); MasterKey key = builder.build();
Damit wird ein Schlüssel mit dem Alias diary aus dem Android-KeyStore gelesen. Existiert er noch nicht, wird er automatisch angelegt. Der Android-KeyStore ist die bevorzugte Stelle, um kryptografische Schlüssel zu speichern, die nur von der App selbst benötigt werden. Informationen, die zwischen mehreren Apps geteilt werden sollen, beispielsweise Anmeldedaten bei Webdiensten, sollte man in der KeyChain ablegen. Das KeyStore-API erlaubt keinen Zugriff auf die Bytes des generierten Schlüssels – so kommen App-Entwickler gar nicht erst in Versuchung, diese irgendwo unsicher abzulegen.
Tatsächlich erfolgen die kryptografischen Operationen in einem sogenannten Trusted Execution Environment (TEE), das ist ein eigenes, unabhängiges System, das auf allen aktuellen ARM-Prozessoren vorhanden ist und selbst bei einem Angriff auf den Kernel – Rooten oder Jailbreaken etwa – gesichert bleibt. Es enthält einen nicht auslesbaren Schlüssel, mit dem die von den Apps verwendeten und im Dateisystem gespeicherten kryptografischen Schlüssel verschlüsselt sind. Ab Android 9 gibt es auf manchen Geräten einen separaten Sicherheits-Chip (Titan M) mit noch höheren Sicherheitsanforderungen, dessen Verwendung der App-Entwickler mittels setIsStrongBoxBacked(true) anfordern kann.
Nur die App, die den Schlüssel angelegt hat, darf ihn verwenden. Selbst wenn ein Angreifer eine App mit demselben Package-Namen erzeugt, kann diese den Schlüssel nicht auslesen, denn sie ist anders signiert: Android erlaubt die Installation einer App mit demselben Package und einer anderen Signatur nur nach Deinstallation der bestehenden App, und dabei werden auch alle zugehörigen Schlüssel gelöscht.
Optional kann die App den Schlüssel biometrisch schützen; der Nutzer muss dann explizit zustimmen, dass die App den Schlüssel verwenden darf. Das schützt gegen Attacken durch böswillige Apps im Hintergrund. Die letzte erfolgreiche Authentifizierung darf nicht länger als die angegebene Anzahl Sekunden zurückliegen: if (App.hasBiometricProtection()) builder.setUserAuthenticationRequired(true, timeoutSeconds). In diesem Fall muss die App auf eine UserNotAuthenticatedException reagieren, etwa indem sie erneut den oben beschriebenen BiometricPrompt anzeigt.
Mit dem MasterKey ist die Verschlüsselung von Dateien leicht:
EncryptedFile encryptedFile = new EncryptedFile.Builder(context, file, key, FileEncryptionScheme. AES256_GCM_HKDF_4KB).build(); FileOutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(bytes);
Die Entschlüsselung erfolgt analog:
FileInputStream inputStream = encryptedFile.openFileInput(); inputStream.read(bytes);
Falls man Benutzereinstellungen geschützt abspeichern will, gibt es eine spezielle SharedPreferences-Implementierung, die ebenfalls in einer verschlüsselten Datei landet:
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create( context, "secret-prefs", encryptionKey(), EncryptedSharedPreferences. PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences. PrefValueEncryptionScheme. AES256_GCM);
Unterschrift
Zu Demonstrationszwecken enthält die App eine Funktion zum Export der Tagebucheinträge in entschlüsselter, signierter Form. Die kryptografische Signatur soll sicherstellen, dass der Inhalt authentisch ist und nicht nachträglich verändert wurde. So sind auch alle Apps digital signiert, um sie gegen unautorisiertes Patchen zu schützen. Anders als bei der symmetrischen Verschlüsselung, bei der man denselben Schlüssel zum Ver- und Entschlüsseln verwendet, gibt es bei Signaturen zwei Schlüssel [2]: einen privaten, unbedingt geheim zu haltenden Schlüssel, mit dem die Signatur erzeugt wird, und einen öffentlichen, über einen sicheren Kanal – beispielsweise die eigene Website – verbreiteten Schlüssel, mit dem sich die Signatur überprüfen lässt. Häufig ist dieser öffentliche Schlüssel seinerseits von einer vertrauenswürdigen Instanz (Certificate Authority) signiert.
Das Schlüsselpaar erzeugt man mit einem KeyPairGenerator (siehe Listing). Der PrivateKeyEntry enthält dabei ein Paar aus öffentlichem und privatem Schlüssel, wobei die App nur die Bytes des öffentlichen Schlüssels auslesen kann. Android erzeugt für jedes Gerät, auf dem die App läuft, einen individuellen privaten Schlüssel, der wie bei der symmetrischen Verschlüsselung niemals den gesicherten Hardware-Bereich verlässt. Mit dem privateKey erzeugt man die Signatur und mit dem publicKey verifiziert man sie.
Alternativ kann der Nutzer die Echtheit eines Textes am PC mit dem openssl-Toolset überprüfen. Ist der öffentliche Schlüssel in der Datei publickey.der gespeichert und die zu überprüfende Signatur in entry.txt.sha256, lautet der Aufruf
openssl dgst -sha256 -keyform der -verify publickey.der -signature entry.txt.sha256 entry.txt
Nach einer Neuinstallation vergibt Android ein neues Schlüsselpaar; den alten öffentlichen Schlüssel sollten sich Nutzer also notieren, wenn sie nach der Neuinstallation noch die Unversehrtheit von alten Texten überprüfen wollen.
Fazit
Auch wenn Android schon von Haus aus diverse Vorkehrungen trifft, um die Daten des Benutzers vor Angriffen von außen zu schützen, kann es für App-Entwickler sinnvoll sein, besonders wichtige Daten extra zu sichern. Mit den Bibliotheken androidx.biometric und androidx.security ist das in wenigen Zeilen erledigt. Die sichere Erzeugung und Verwaltung der verwendeten kryptografischen Schlüssel übernimmt das System. Exportierte Daten, die nicht verändert werden dürfen, lassen sich mit digitalen Signaturen schützen. Auch das ist mit den Android-APIs so einfach, dass man es als Entwickler viel häufiger tun sollte.
(jow@ct.de)
Tagebuch-App auf GitHub und weitere Links: ct.de/yper