Advertisement
  1. Code
  2. Security

Sicheres Speichern von Daten unter Android

by
Read Time:11 minsLanguages:

German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)

Die Glaubwürdigkeit einer App hängt heute stark davon ab, wie die privaten Daten des Benutzers verwaltet werden. Der Android-Stack verfügt über viele leistungsstarke APIs für die Speicherung von Anmeldeinformationen und Schlüsseln. Bestimmte Funktionen sind nur in bestimmten Versionen verfügbar. Diese kurze Serie beginnt mit einem einfachen Ansatz für die Inbetriebnahme, indem Sie sich das Speichersystem ansehen und wissen, wie vertrauliche Daten über einen vom Benutzer angegebenen Passcode verschlüsselt und gespeichert werden. Im zweiten Tutorial werden komplexere Möglichkeiten zum Schutz von Schlüsseln und Anmeldeinformationen vorgestellt.

Die Grundlagen

Die erste Frage, über die Sie nachdenken müssen, ist, wie viele Daten Sie tatsächlich erfassen müssen. Ein guter Ansatz ist es, das Speichern privater Daten zu vermeiden, wenn Sie dies nicht wirklich müssen.

Für Daten, die Sie speichern müssen, hilft Ihnen die Android-Architektur. Seit 6.0 Marshmellow ist die Vollplattenverschlüsselung für Geräte mit dieser Funktion standardmäßig aktiviert. Dateien und SharedPreferences, die von der App gespeichert werden, werden automatisch mit der Konstante MODE_PRIVATE festgelegt. Dies bedeutet, dass auf die Daten nur von Ihrer eigenen App zugegriffen werden kann.

Es ist eine gute Idee, sich an diese Standardeinstellung zu halten. Sie können es explizit festlegen, wenn Sie eine gemeinsame Einstellung speichern.

Oder beim Speichern einer Datei.

Vermeiden Sie das Speichern von Daten im externen Speicher, da diese dann für andere Apps und Benutzer sichtbar sind. Um zu verhindern, dass Benutzer das Kopieren Ihrer App-Binärdateien und -Daten erschweren, können Sie Benutzern untersagen, die App auf einem externen Speicher zu installieren. Durch Hinzufügen von android:installLocation mit dem Wert internalOnly zur Manifestdatei wird dies erreicht.

Sie können auch verhindern, dass die App und ihre Daten gesichert werden. Dies verhindert auch das Herunterladen des Inhalts des privaten Datenverzeichnisses einer App mithilfe von adb backup. Setzen Sie dazu das Attribut android: allowBackup in der Manifestdatei auf false. Standardmäßig ist dieses Attribut auf true festgelegt.

Dies sind bewährte Methoden, die jedoch für ein kompromittiertes oder gerootetes Gerät nicht funktionieren. Die Festplattenverschlüsselung ist nur dann nützlich, wenn das Gerät mit einem Sperrbildschirm gesichert ist. Hier ist ein app-seitiges Passwort von Vorteil, das seine Daten durch Verschlüsselung schützt.

Benutzerdaten mit einem Passwort sichern

Conceal ist eine gute Wahl für eine Verschlüsselungsbibliothek, da Sie damit sehr schnell einsatzbereit sind, ohne sich um die zugrunde liegenden Details kümmern zu müssen. Ein Exploit für ein beliebtes Framework wirkt sich jedoch gleichzeitig auf alle Apps aus, die darauf angewiesen sind.

Es ist auch wichtig, über die Funktionsweise von Verschlüsselungssystemen informiert zu sein, um feststellen zu können, ob Sie ein bestimmtes Framework sicher verwenden. Für diesen Beitrag werden wir uns die Hände schmutzig machen, indem wir uns direkt an den Kryptografieanbieter wenden.

AES und passwortbasierte Schlüsselableitung

Wir werden den empfohlenen AES-Standard verwenden, der Daten mit einem Schlüssel verschlüsselt. Der gleiche Schlüssel, der zum Verschlüsseln der Daten verwendet wird, wird zum Entschlüsseln der Daten verwendet, was als symmetrische Verschlüsselung bezeichnet wird. Es gibt verschiedene Schlüsselgrößen, und AES256 (256 Bit) ist die bevorzugte Länge für die Verwendung mit vertraulichen Daten.

Während die Benutzererfahrung Ihrer App einen Benutzer zwingen sollte, einen starken Passcode zu verwenden, besteht die Möglichkeit, dass derselbe Passcode auch von einem anderen Benutzer ausgewählt wird. Es ist nicht sicher, die Sicherheit unserer verschlüsselten Daten in die Hände des Benutzers zu legen. Unsere Daten müssen stattdessen mit einem Schlüssel gesichert werden, der zufällig und groß genug ist (dh genügend Entropie aufweist), um als stark angesehen zu werden. Aus diesem Grund wird niemals empfohlen, ein Kennwort direkt zum Verschlüsseln von Daten zu verwenden. Hier kommt eine Funktion namens Kennwortbasierte Schlüsselableitungsfunktion (PBKDF2) ins Spiel.

PDKDF2 leitet einen Schlüssel von einem Passwort ab, indem es mehrmals mit einem Salz gehasht wird. Dies wird als Schlüsseldehnung bezeichnet. Das Salt ist nur eine zufällige Folge von Daten und macht den abgeleiteten Schlüssel eindeutig, selbst wenn dasselbe Passwort von jemand anderem verwendet wurde. Beginnen wir mit der Erzeugung dieses Salzes.

Die SecureRandom-Klasse garantiert, dass die generierte Ausgabe schwer vorherzusagen ist - es handelt sich um einen "kryptografisch starken Zufallszahlengenerator". Wir können jetzt das Salt und das Passwort in ein passwortbasiertes Verschlüsselungsobjekt einfügen: PBEKeySpec. Der Konstruktor des Objekts nimmt auch eine Iterationszählform an, wodurch der Schlüssel stärker wird. Dies liegt daran, dass durch Erhöhen der Anzahl der Iterationen die Zeit verlängert wird, die für die Bearbeitung eines Schlüsselsatzes während eines Brute-Force-Angriffs erforderlich ist. Die PBEKeySpec wird dann an die SecretKeyFactory übergeben, die den Schlüssel schließlich als byte[] -Array generiert. Wir werden dieses Raw-byte[]-Array in ein SecretKeySpec-Objekt einbinden.

Beachten Sie, dass das Kennwort als char[] -Array übergeben wird und von der PBEKeySpec-Klasse auch als char[] -Array gespeichert wird. char[] -Arrays werden normalerweise für Verschlüsselungsfunktionen verwendet, da während die String-Klasse unveränderlich ist, ein char[] -Array mit vertraulichen Informationen überschrieben werden kann, wodurch die vertraulichen Daten vollständig aus dem Phyc-RAM des Geräts entfernt werden.

Initialisierungsvektoren

Wir sind jetzt bereit, die Daten zu verschlüsseln, aber wir müssen noch etwas tun. Es gibt verschiedene Verschlüsselungsmodi mit AES, aber wir werden den empfohlenen verwenden: CBC (Cipher Block Chaining). Dies bearbeitet unsere Daten blockweise. Das Tolle an diesem Modus ist, dass jeder nächste unverschlüsselte Datenblock mit dem vorherigen verschlüsselten Block XOR-verknüpft ist, um die Verschlüsselung zu verbessern. Dies bedeutet jedoch, dass der erste Block niemals so einzigartig ist wie alle anderen!

Wenn eine zu verschlüsselnde Nachricht genauso wie eine andere zu verschlüsselnde Nachricht beginnen würde, wäre die beginnende verschlüsselte Ausgabe dieselbe, und dies würde einem Angreifer einen Hinweis darauf geben, wie die Nachricht aussehen könnte. Die Lösung besteht darin, einen Initialisierungsvektor(IV) zu verwenden.

Eine IV ist nur ein Block von zufälligen Bytes, die mit dem ersten Block von Benutzerdaten XOR-verknüpft werden. Da jeder Block von allen bis zu diesem Zeitpunkt verarbeiteten Blöcken abhängt, wird die gesamte Nachricht eindeutig verschlüsselt. Identische Nachrichten, die mit demselben Schlüssel verschlüsselt wurden, führen nicht zu identischen Ergebnissen. Erstellen wir jetzt eine IV.

Ein Hinweis zu SecureRandom. In den Versionen 4.3 und darunter war die Java Cryptography Architecture aufgrund einer fehlerhaften Initialisierung des zugrunde liegenden Pseudozufallszahlengenerators (PRNG) anfällig. Wenn Sie auf Versionen 4.3 und darunter abzielen, ist ein Fix verfügbar.

Daten verschlüsseln

Mit einer IvParameterSpec können wir jetzt die eigentliche Verschlüsselung durchführen.

Hier übergeben wir die Zeichenfolge "AES / CBC / PKCS7Padding". Dies gibt die AES-Verschlüsselung mit Chiffrierblockverkettung an. Der letzte Teil dieser Zeichenfolge bezieht sich auf PKCS7, einen etablierten Standard zum Auffüllen von Daten, die nicht perfekt in die Blockgröße passen. (Blöcke sind 128 Bit und das Auffüllen erfolgt vor der Verschlüsselung.)

Um unser Beispiel zu vervollständigen, fügen wir diesen Code in eine Verschlüsselungsmethode ein, die das Ergebnis in eine HashMap packt, die die verschlüsselten Daten zusammen mit dem für die Entschlüsselung erforderlichen Salt- und Initialisierungsvektor enthält.

Die Entschlüsselungsmethode

Sie müssen nur die Infusion und das Salz mit Ihren Daten speichern. Während Salze und Infusionen als öffentlich gelten, stellen Sie sicher, dass sie nicht nacheinander erhöht oder wiederverwendet werden. Um die Daten zu entschlüsseln, müssen wir lediglich den Modus im Cipher-Konstruktor von ENCRYPT_MODE in DECRYPT_MODE ändern. Die Entschlüsselungsmethode verwendet eine HashMap, die dieselben erforderlichen Informationen enthält (verschlüsselte Daten, Salt und IV), und gibt ein entschlüsseltes byte[] -Array mit dem richtigen Kennwort zurück. Die Entschlüsselungsmethode generiert den Verschlüsselungsschlüssel aus dem Kennwort neu. Der Schlüssel sollte niemals aufbewahrt werden!

Testen der Verschlüsselung und Entschlüsselung

Um das Beispiel einfach zu halten, lassen wir die Fehlerprüfung aus, die sicherstellen würde, dass die HashMap die erforderlichen Schlüssel-Wert-Paare enthält. Wir können jetzt unsere Methoden testen, um sicherzustellen, dass die Daten nach der Verschlüsselung korrekt entschlüsselt werden.

Die Methoden verwenden ein byte[] -Array, sodass Sie beliebige Daten anstelle von nur String-Objekten verschlüsseln können.

Verschlüsselte Daten speichern

Nachdem wir ein verschlüsseltes byte[] -Array haben, können wir es im Speicher speichern.

Wenn Sie IV und Salt nicht separat speichern möchten, kann HashMap mit den Klassen ObjectInputStream und ObjectOutputStream serialisiert werden.

Speichern sicherer Daten in SharedPreferences

Sie können auch sichere Daten in den SharedPreferences Ihrer App speichern.

Da SharedPreferences ein XML-System ist, das nur bestimmte Grundelemente und Objekte als Werte akzeptiert, müssen wir unsere Daten in ein kompatibles Format wie ein String-Objekt konvertieren. Mit Base64 können wir die Rohdaten in eine String-Darstellung konvertieren, die nur die vom XML-Format zugelassenen Zeichen enthält. Verschlüsseln Sie sowohl den Schlüssel als auch den Wert, damit ein Angreifer nicht herausfinden kann, wofür ein Wert sein könnte. Im obigen Beispiel sind encryptedKey und encryptedValue beide verschlüsselte byte[]-Arrays, die von unserer encryptBytes()-Methode zurückgegeben werden. IV und Salt können in der Voreinstellungsdatei oder als separate Datei gespeichert werden. Um die verschlüsselten Bytes aus den SharedPreferences zurückzugewinnen, können wir eine Base64-Decodierung auf den gespeicherten String anwenden.

Unsichere Daten aus alten Versionen löschen

Nachdem die gespeicherten Daten jetzt sicher sind, kann es sein, dass Sie eine frühere Version der App haben, in der die Daten unsicher gespeichert wurden. Bei einem Upgrade könnten die Daten gelöscht und neu verschlüsselt werden. Der folgende Code löscht eine Datei mit zufälligen Daten.

Theoretisch können Sie Ihre freigegebenen Einstellungen einfach löschen, indem Sie die Dateien /data/data/com.your.package.name/shared_prefs/your_prefs_name.xml und your_prefs_name.bak entfernen und die speicherinternen Einstellungen mit dem folgenden Code löschen:

Anstatt jedoch zu versuchen, die alten Daten zu löschen und zu hoffen, dass sie funktionieren, ist es besser, sie zuerst zu verschlüsseln! Dies gilt insbesondere im Allgemeinen für Solid-State-Laufwerke, die häufig Datenschreibvorgänge auf verschiedene Regionen verteilen, um Verschleiß zu vermeiden. Dies bedeutet, dass der physische Solid-State-Speicher Ihre Daten möglicherweise an seinem ursprünglichen Speicherort auf der Festplatte beibehält, selbst wenn Sie eine Datei im Dateisystem überschreiben.

Abschluss

Damit ist unser Tutorial zum Speichern verschlüsselter Daten abgeschlossen. In diesem Beitrag haben Sie gelernt, wie Sie vertrauliche Daten mit einem vom Benutzer angegebenen Kennwort sicher verschlüsseln und entschlüsseln. Es ist einfach, wenn Sie wissen, wie es geht, aber es ist wichtig, alle Best Practices zu befolgen, um sicherzustellen, dass die Daten Ihrer Benutzer wirklich sicher sind.

Im nächsten Beitrag werden wir uns ansehen, wie Sie den KeyStore und andere APIs für Anmeldeinformationen nutzen können, um Elemente sicher zu speichern. In der Zwischenzeit lesen Sie einige unserer anderen großartigen Artikel zur Entwicklung von Android-Apps.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.