Was ist der am besten geeignete Weg, um Benutzereinstellungen in der Android-Anwendung zu speichern


308

Ich erstelle eine Anwendung, die über Benutzername / Passwort eine Verbindung zum Server herstellt, und möchte die Option "Passwort speichern" aktivieren, damit der Benutzer das Passwort nicht bei jedem Start der Anwendung eingeben muss.

Ich habe versucht, dies mit freigegebenen Einstellungen zu tun, bin mir jedoch nicht sicher, ob dies die beste Lösung ist.

Ich würde mich über jeden Vorschlag zum Speichern von Benutzerwerten / -einstellungen in der Android-Anwendung freuen.

Antworten:


233

Im Allgemeinen sind SharedPreferences die beste Wahl zum Speichern von Einstellungen. Im Allgemeinen würde ich diesen Ansatz zum Speichern von Anwendungs- und Benutzereinstellungen empfehlen.

Das einzige Problem ist, was Sie sparen. Das Speichern von Passwörtern ist immer schwierig, und ich würde sie besonders vorsichtig als Klartext speichern. Die Android-Architektur ist so konzipiert, dass die SharedPreferences Ihrer Anwendung in einer Sandbox gespeichert sind, um zu verhindern, dass andere Anwendungen auf die Werte zugreifen können, sodass dort eine gewisse Sicherheit besteht. Der physische Zugriff auf ein Telefon kann jedoch möglicherweise den Zugriff auf die Werte ermöglichen.

Wenn möglich, würde ich in Betracht ziehen, den Server so zu ändern, dass ein ausgehandeltes Token für die Bereitstellung des Zugriffs verwendet wird, beispielsweise OAuth . Alternativ müssen Sie möglicherweise eine Art kryptografischen Speicher erstellen, obwohl dies nicht trivial ist. Stellen Sie zumindest sicher, dass Sie das Kennwort verschlüsseln, bevor Sie es auf die Festplatte schreiben.


3
Könnten Sie bitte erklären, was Sie unter Sandbox verstehen?
Abhijit

14
Ein Sandbox-Programm ist eine Anwendung, deren Prozess und Informationen (z. B. diese gemeinsamen Einstellungen) vor den übrigen Anwendungen verborgen bleiben. Eine Android-Anwendung, die in einem Paket ausgeführt wird, kann nicht direkt auf etwas in einem anderen Paket zugreifen. Aus diesem Grund können Anwendungen im selben Paket (die immer Ihnen gehören) auf Informationen von anderen
zugreifen

@Reto Meier Meine Anforderung ist es, die öffentlich verfügbaren Webdienste zu schützen, für die ich ein Token verwende. Ist das Speichern in gemeinsam genutzten Einstellungen sicher? Ich habe einen Bootup-Broadcast-Empfänger in meiner Anwendung, der alle gemeinsam genutzten Präferenzdaten löscht, wenn das Gerät als gerootet gefunden wurde. Ist das genug, um meinen Token zu schützen?
Pyus13

1
Per android-developers.blogspot.com/2013/02/... , Benutzerberechtigungsnachweise mit dem MODE_PRIVATE - Flag gespeichert und gespeichert in internen Speicher werden sollte (mit den gleichen Einsprüche über lokal letztlich offen zum Angriff , jede Art von Passwort zu speichern). MODE_PRIVATEIst die Verwendung mit SharedPreferences gleichbedeutend mit der Verwendung einer im internen Speicher erstellten Datei, um lokal gespeicherte Daten zu verschleiern?
Qix

6
Speichern Sie kein Passwort in den freigegebenen Einstellungen. Wenn der Benutzer jemals das Telefon verliert, hat er das Passwort verloren. Es wird gelesen. Wenn sie dieses Passwort an anderer Stelle verwendet haben, ist jeder Ort, an dem sie es verwendet haben, gefährdet. Außerdem haben Sie dieses Konto dauerhaft verloren, da sie mit dem Passwort Ihr Passwort ändern können. Der richtige Weg, dies zu tun, besteht darin, das Passwort einmal an den Server zu senden und ein Anmeldetoken zurück zu erhalten. Speichern Sie dies in gemeinsamen Einstellungen und senden Sie es bei jeder Anfrage. Wenn dieses Token kompromittiert wird, geht nichts anderes verloren.
Gabe Sechan

211

Ich stimme Reto und fiXedd zu. Objektiv gesehen macht es wenig Sinn, viel Zeit und Mühe in die Verschlüsselung von Passwörtern in SharedPreferences zu investieren, da jeder Angreifer, der Zugriff auf Ihre Einstellungsdatei hat, wahrscheinlich auch auf die Binärdatei Ihrer Anwendung und damit auf die Schlüssel zum Entschlüsseln der Kennwörter zugreifen kann Passwort.

Allerdings scheint es eine Werbeinitiative zu geben, die mobile Anwendungen identifiziert, die ihre Passwörter in SharedPreferences im Klartext speichern, und diese Anwendungen ungünstig beleuchtet. Einige Beispiele finden Sie unter http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ und http://viaforensics.com/appwatchdog .

Wir brauchen zwar mehr Aufmerksamkeit für die Sicherheit im Allgemeinen, aber ich würde argumentieren, dass diese Art der Aufmerksamkeit für dieses eine spezielle Thema unsere Gesamtsicherheit nicht wesentlich erhöht. Da die Wahrnehmungen jedoch so sind, wie sie sind, finden Sie hier eine Lösung zum Verschlüsseln der Daten, die Sie in SharedPreferences platzieren.

Wickeln Sie einfach Ihr eigenes SharedPreferences-Objekt in dieses ein, und alle Daten, die Sie lesen / schreiben, werden automatisch verschlüsselt und entschlüsselt. z.B.

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

Hier ist der Code für die Klasse:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}

3
FYI Base64 ist in API Level 8 (2.2) und höher verfügbar. Sie können iharder.sourceforge.net/current/java/base64 oder etwas anderes für frühere Betriebssysteme verwenden.
Emmby

34
Ja, ich habe das geschrieben. Fühlen Sie sich frei zu verwenden, keine Namensnennung erforderlich
emmby

8
Ich stimme mit Ihnen ein. Wenn das Kennwort jedoch nur auf dem Server verwendet wird, warum nicht die Verschlüsselung mit öffentlichen / privaten Schlüsseln verwenden? Öffentlicher Schlüssel auf dem Client beim Speichern des Passworts. Der Client muss das Klartext-Passwort nie wieder lesen, oder? Der Server kann es dann mit dem privaten Schlüssel entschlüsseln. Selbst wenn jemand Ihren App-Quellcode durchgeht, kann er das Passwort nicht erhalten, es sei denn, er hackt Ihren Server und erhält den privaten Schlüssel.
Patrick Boos

4
Ich habe diesem Code einige Funktionen hinzugefügt und ihn auf github unter github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/… platziert . Jetzt werden unverschlüsselte Einstellungen auf die verschlüsselten Einstellungen migriert. Außerdem wird der Schlüssel zur Laufzeit generiert, sodass beim Dekompilieren der App der Schlüssel nicht freigegeben wird.
RightHandedMonkey

3
Späte Hinzufügung, aber der Kommentar von @PatrickBoos ist eine großartige Idee. Ein Problem dabei ist jedoch, dass sich ein Angreifer, der diese Verschlüsselung gestohlen hat, trotz der Verschlüsselung des Kennworts weiterhin bei Ihren Servern anmelden kann, da Ihre Server die Entschlüsselung durchführen. Eine Ergänzung zu diesem Ansatz besteht darin, das Kennwort zusammen mit einem Zeitstempel zu verschlüsseln. Auf diese Weise können Sie beispielsweise festlegen, dass nur in der jüngeren Vergangenheit gespeicherte Kennwörter zugelassen werden (z. B. das Hinzufügen eines Ablaufdatums zu Ihrem "Token") oder dass bestimmte Benutzer seit einem bestimmten Datum einen Zeitstempel benötigen (lassen Sie uns "widerrufen"). alte "Token").
Adevine

29

Der einfachste Weg, eine einzelne Voreinstellung in einer Android-Aktivität zu speichern, besteht darin, Folgendes zu tun:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

Wenn Sie sich Sorgen um die Sicherheit dieser Kennwörter machen, können Sie das Kennwort jederzeit verschlüsseln, bevor Sie es speichern.


9
Ich könnte Ihnen nicht mehr über diesen simplen Ansatz zustimmen. Sie sollten sich jedoch immer Sorgen um die Sicherheit der von Ihnen gespeicherten Passwörter machen. Abhängig von Ihrer Anwendung haben Sie potenzielle Verbindlichkeiten für gestohlene persönliche Daten. Ich möchte nur darauf hinweisen, wenn jemand versucht, tatsächliche Passwörter für Dinge wie Bankkonten oder etwas ähnlich Wichtiges zu speichern. Ich stimme dich trotzdem ab.
While-E

1
Wo würden Sie den Schlüssel speichern, in dem das Passwort gespeichert ist? Wenn andere Benutzer auf die freigegebenen Einstellungen zugreifen können, gilt dies auch für den Schlüssel.
OrhanC1

@ OrhanC1 hast du die Antwort bekommen?
eRaisedToX

10

Mit dem von Richard bereitgestellten Snippet können Sie das Passwort verschlüsseln, bevor Sie es speichern. Die Voreinstellungs-API bietet jedoch keine einfache Möglichkeit, den Wert abzufangen und zu verschlüsseln. Sie können das Speichern über einen OnPreferenceChange-Listener blockieren und theoretisch über einen PreferenceChangeListener ändern. Dies führt jedoch zu einer Endlosschleife.

Ich hatte zuvor vorgeschlagen, eine "versteckte" Präferenz hinzuzufügen, um dies zu erreichen. Es ist definitiv nicht der beste Weg. Ich werde zwei weitere Optionen vorstellen, die ich für praktikabler halte.

Das einfachste ist, dass Sie in einem PreferenceChangeListener den eingegebenen Wert abrufen, verschlüsseln und dann in einer alternativen Voreinstellungsdatei speichern können:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

Die zweite und jetzt bevorzugte Methode besteht darin, eine eigene benutzerdefinierte Einstellung zu erstellen, indem Sie EditTextPreference erweitern, die Methoden setText()und überschreiben getText(), sodass setText()das Kennwort verschlüsselt wird und getText()null zurückgegeben wird.


Ich weiß, dass dies ziemlich alt ist, aber würde es Ihnen etwas ausmachen, Ihren Code für Ihre benutzerdefinierte Version von EditTextPreference zu veröffentlichen?
RenniePet

Egal, ich habe hier ein brauchbares Beispiel gefunden groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M und ich habe es jetzt zum Laufen gebracht . Vielen Dank, dass Sie diesen Ansatz vorgeschlagen haben.
RenniePet

6

In Ordnung; Es ist schon eine Weile her, dass die Antwort irgendwie gemischt ist, aber hier sind ein paar allgemeine Antworten. Ich habe das wie verrückt recherchiert und es war schwer, eine gute Antwort zu finden

  1. Die MODE_PRIVATE-Methode gilt als allgemein sicher, wenn Sie davon ausgehen, dass der Benutzer das Gerät nicht als Root angemeldet hat. Ihre Daten werden im Klartext in einem Teil des Dateisystems gespeichert, auf den nur das ursprüngliche Programm zugreifen kann. Dies erleichtert das Abrufen des Kennworts mit einer anderen App auf einem gerooteten Gerät. Möchten Sie gerootete Geräte unterstützen?

  2. AES ist immer noch die beste Verschlüsselung, die Sie durchführen können. Denken Sie daran, dies nachzuschlagen, wenn Sie eine neue Implementierung starten, wenn es eine Weile her ist, seit ich dies veröffentlicht habe. Das größte Problem dabei ist "Was tun mit dem Verschlüsselungsschlüssel?"

Jetzt sind wir also bei "Was tun mit dem Schlüssel?" Portion. Das ist der schwierige Teil. Es stellt sich heraus, dass es nicht so schlimm ist, den Schlüssel zu bekommen. Sie können eine Schlüsselableitungsfunktion verwenden, um ein Kennwort zu verwenden und es zu einem ziemlich sicheren Schlüssel zu machen. Sie haben Probleme wie "Wie viele Durchgänge machen Sie mit PKFDF2?", Aber das ist ein anderes Thema

  1. Im Idealfall speichern Sie den AES-Schlüssel außerhalb des Geräts. Sie müssen jedoch einen guten Weg finden, um den Schlüssel sicher, zuverlässig und sicher vom Server abzurufen

  2. Sie haben eine Art Anmeldesequenz (sogar die ursprüngliche Anmeldesequenz, die Sie für den Fernzugriff ausführen). Sie können zwei Durchläufe Ihres Schlüsselgenerators mit demselben Kennwort ausführen. Dies funktioniert so, dass Sie den Schlüssel zweimal mit einem neuen Salt und einem neuen sicheren Initialisierungsvektor ableiten. Sie speichern eines dieser generierten Kennwörter auf dem Gerät und verwenden das zweite Kennwort als AES-Schlüssel.

Wenn Sie sich anmelden, leiten Sie den Schlüssel bei der lokalen Anmeldung erneut ab und vergleichen ihn mit dem gespeicherten Schlüssel. Sobald dies erledigt ist, verwenden Sie den Ableitungsschlüssel Nr. 2 für AES.

  1. Mit dem "allgemein sicheren" Ansatz verschlüsseln Sie die Daten mit AES und speichern den Schlüssel in MODE_PRIVATE. Dies wird von einem aktuellen Android-Blogbeitrag empfohlen. Nicht unglaublich sicher, aber für manche Menschen viel besser als einfacher Text

Sie können viele Variationen davon machen. Beispielsweise können Sie anstelle einer vollständigen Anmeldesequenz eine schnelle PIN (abgeleitet) eingeben. Die schnelle PIN ist möglicherweise nicht so sicher wie eine vollständige Anmeldesequenz, aber um ein Vielfaches sicherer als einfacher Text


5

Ich weiß, dass dies ein bisschen Nekromantie ist, aber Sie sollten den Android AccountManager verwenden . Es wurde speziell für dieses Szenario entwickelt. Es ist etwas umständlich, aber eines der Dinge, die es tut, ist, die lokalen Anmeldeinformationen ungültig zu machen, wenn sich die SIM-Karte ändert. Wenn also jemand Ihr Telefon klaut und eine neue SIM-Karte hineinwirft, werden Ihre Anmeldeinformationen nicht gefährdet.

Dies gibt dem Benutzer auch eine schnelle und einfache Möglichkeit, von einem Ort aus auf die gespeicherten Anmeldeinformationen für jedes Konto auf dem Gerät zuzugreifen (und diese möglicherweise zu löschen).

SampleSyncAdapter ist ein Beispiel, das gespeicherte Kontoanmeldeinformationen verwendet.


1
Bitte beachten Sie, dass die Verwendung des AccountManager nicht sicherer ist als jede andere oben angegebene Methode! developer.android.com/training/id-auth/…
Sander Versluys

1
Der Anwendungsfall für AccountManager ist, wenn das Konto zwischen verschiedenen Apps und Apps verschiedener Autoren geteilt werden muss. Das Speichern des Passworts und das Weitergeben an eine anfordernde App wäre nicht angemessen. Wenn der Benutzer / das Kennwort nur für eine einzelne App verwendet wird, verwenden Sie AccountManager nicht.
Dolmen

1
@dolmen, das ist nicht ganz richtig. Der AccountManager gibt das Kontokennwort nicht an eine App weiter, deren UID nicht mit der des Authenticators übereinstimmt. Der Name, ja; das Auth-Token, ja; das Passwort, nein. Wenn Sie es versuchen, wird eine SecurityException ausgelöst. Und der Anwendungsfall ist viel umfassender. developer.android.com/training/id-auth/identify.html
Jon O

5

Ich werde meinen Hut in den Ring werfen, um über das Sichern von Passwörtern im Allgemeinen auf Android zu sprechen. Unter Android sollte die Gerätebinärdatei als kompromittiert betrachtet werden. Dies gilt auch für alle Endanwendungen, die direkt vom Benutzer gesteuert werden. Konzeptionell könnte ein Hacker den erforderlichen Zugriff auf die Binärdatei verwenden, um sie zu dekompilieren und Ihre verschlüsselten Passwörter usw. auszurotten.

Daher gibt es zwei Vorschläge, die ich gerne veröffentlichen möchte, wenn die Sicherheit für Sie ein wichtiges Anliegen ist:

1) Speichern Sie nicht das tatsächliche Passwort. Speichern Sie ein gewährtes Zugriffstoken und verwenden Sie das Zugriffstoken und die Signatur des Telefons, um die Sitzungsserverseite zu authentifizieren. Dies hat den Vorteil, dass Sie das Token auf eine begrenzte Dauer beschränken können, das ursprüngliche Kennwort nicht gefährden und über eine gute Signatur verfügen, mit der Sie später mit dem Datenverkehr korrelieren können (um beispielsweise nach Eindringversuchen zu suchen und das Token ungültig zu machen Token, der es unbrauchbar macht).

2) Verwenden Sie die 2-Faktor-Authentifizierung. Dies mag ärgerlicher und aufdringlicher sein, ist jedoch in einigen Compliance-Situationen unvermeidbar.



2

Dies ist eine ergänzende Antwort für diejenigen, die aufgrund des Fragentitels hierher kommen (wie ich) und sich nicht mit den Sicherheitsproblemen im Zusammenhang mit dem Speichern von Passwörtern befassen müssen.

So verwenden Sie freigegebene Einstellungen

Benutzereinstellungen werden in der Regel lokal in Android SharedPreferencesmit einem Schlüssel-Wert-Paar gespeichert . Sie verwenden dieString Schlüssel speichern oder suchen Sie den zugehörigen Wert.

Schreiben Sie in die freigegebenen Einstellungen

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Verwenden Sie apply()anstelle voncommit() zum Speichern im Hintergrund und nicht sofort.

Lesen Sie aus den freigegebenen Einstellungen

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

Der Standardwert wird verwendet, wenn der Schlüssel nicht gefunden wird.

Anmerkungen

  • Anstatt wie oben beschrieben eine lokale Schlüsselzeichenfolge an mehreren Stellen zu verwenden, ist es besser, eine Konstante an einer einzelnen Stelle zu verwenden. Sie können so etwas oben in Ihrer Einstellungsaktivität verwenden:

    final static String PREF_MY_INT_KEY = "myInt";
  • Ich pflegte , eine intin meinem Beispiel, aber Sie können auch verwenden putString(), putBoolean(), getString(), getBoolean()etc.

  • Weitere Informationen finden Sie in der Dokumentation .
  • Es gibt mehrere Möglichkeiten, SharedPreferences abzurufen. In dieser Antwort erfahren Sie, worauf Sie achten müssen.

1

Diese Antwort basiert auf einem vorgeschlagenen Ansatz von Mark. Es wird eine benutzerdefinierte Version der EditTextPreference-Klasse erstellt, die zwischen dem in der Ansicht angezeigten Klartext und einer verschlüsselten Version des im Voreinstellungsspeicher gespeicherten Kennworts hin und her konvertiert.

Wie die meisten, die auf diesen Thread geantwortet haben, darauf hingewiesen haben, ist dies keine sehr sichere Technik, obwohl der Sicherheitsgrad teilweise vom verwendeten Verschlüsselungs- / Entschlüsselungscode abhängt. Aber es ist ziemlich einfach und bequem und verhindert das lässigste Schnüffeln.

Hier ist der Code für die benutzerdefinierte EditTextPreference-Klasse:

package com.Merlinia.OutBack_Client;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.util.Base64;

import com.Merlinia.MEncryption_Main.MEncryptionUserPassword;


/**
 * This class extends the EditTextPreference view, providing encryption and decryption services for
 * OutBack user passwords. The passwords in the preferences store are first encrypted using the
 * MEncryption classes and then converted to string using Base64 since the preferences store can not
 * store byte arrays.
 *
 * This is largely copied from this article, except for the encryption/decryption parts:
 * https://groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M
 */
public class EditPasswordPreference  extends EditTextPreference {

    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context) {
        super(context);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet, int defaultStyle) {
        super(context, attributeSet, defaultStyle);
    }


    /**
     * Override the method that gets a preference from the preferences storage, for display by the
     * EditText view. This gets the base64 password, converts it to a byte array, and then decrypts
     * it so it can be displayed in plain text.
     * @return  OutBack user password in plain text
     */
    @Override
    public String getText() {
        String decryptedPassword;

        try {
            decryptedPassword = MEncryptionUserPassword.aesDecrypt(
                     Base64.decode(getSharedPreferences().getString(getKey(), ""), Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
            decryptedPassword = "";
        }

        return decryptedPassword;
    }


    /**
     * Override the method that gets a text string from the EditText view and stores the value in
     * the preferences storage. This encrypts the password into a byte array and then encodes that
     * in base64 format.
     * @param passwordText  OutBack user password in plain text
     */
    @Override
    public void setText(String passwordText) {
        byte[] encryptedPassword;

        try {
            encryptedPassword = MEncryptionUserPassword.aesEncrypt(passwordText);
        } catch (Exception e) {
            e.printStackTrace();
            encryptedPassword = new byte[0];
        }

        getSharedPreferences().edit().putString(getKey(),
                                          Base64.encodeToString(encryptedPassword, Base64.DEFAULT))
                .commit();
    }


    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        if (restoreValue)
            getEditText().setText(getText());
        else
            super.onSetInitialValue(restoreValue, defaultValue);
    }
}

Dies zeigt, wie es verwendet werden kann - dies ist die "Elemente" -Datei, die die Anzeige der Einstellungen steuert. Beachten Sie, dass es drei normale EditTextPreference-Ansichten und eine der benutzerdefinierten EditPasswordPreference-Ansichten enthält.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <EditTextPreference
        android:key="@string/useraccountname_key"
        android:title="@string/useraccountname_title"
        android:summary="@string/useraccountname_summary"
        android:defaultValue="@string/useraccountname_default"
        />

    <com.Merlinia.OutBack_Client.EditPasswordPreference
        android:key="@string/useraccountpassword_key"
        android:title="@string/useraccountpassword_title"
        android:summary="@string/useraccountpassword_summary"
        android:defaultValue="@string/useraccountpassword_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverip_key"
        android:title="@string/outbackserverip_title"
        android:summary="@string/outbackserverip_summary"
        android:defaultValue="@string/outbackserverip_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverport_key"
        android:title="@string/outbackserverport_title"
        android:summary="@string/outbackserverport_summary"
        android:defaultValue="@string/outbackserverport_default"
        />

</PreferenceScreen>

Die eigentliche Verschlüsselung / Entschlüsselung bleibt dem Leser als Übung. Ich verwende derzeit Code basierend auf diesem Artikel http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , allerdings mit unterschiedlichen Werten für den Schlüssel und den Initialisierungsvektor.


1

Zunächst denke ich, dass Benutzerdaten nicht auf dem Telefon gespeichert werden sollten, und wenn Daten irgendwo auf dem Telefon gespeichert werden müssen, sollten sie mit den privaten Daten der Apps verschlüsselt werden. Die Sicherheit der Benutzeranmeldeinformationen sollte die Priorität der Anwendung sein.

Die sensiblen Daten sollten sicher oder gar nicht gespeichert werden. Im Falle einer verlorenen Geräte- oder Malware-Infektion können unsicher gespeicherte Daten gefährdet werden.


1

Ich verwende den Android KeyStore, um das Passwort mit RSA im EZB-Modus zu verschlüsseln und es dann in den SharedPreferences zu speichern.

Wenn ich das Passwort zurück haben möchte, lese ich das verschlüsselte aus den SharedPreferences und entschlüssele es mit dem KeyStore.

Mit dieser Methode generieren Sie ein öffentliches / privates Schlüsselpaar, in dem das private sicher gespeichert und von Android verwaltet wird.

Hier ist ein Link dazu: Android KeyStore Tutorial


-2

Gemeinsame Einstellungen sind der einfachste Weg, um unsere Anwendungsdaten zu speichern. Es ist jedoch möglich, dass jeder unsere gemeinsamen Einstellungsdaten über den Anwendungsmanager löschen kann. Daher denke ich nicht, dass dies für unsere Anwendung völlig sicher ist.


-3

Sie müssen das SQLite-Sicherheits-Apit verwenden, um die Kennwörter zu speichern. Hier ist das beste Beispiel, in dem Passwörter gespeichert werden. Hier ist der Link für die Quelle und Erklärung - http://code.google.com/p/android-passwordsafe/


3
Das OP muss ein Paar aus Benutzername und Passwort speichern. Es wäre lächerlich, eine vollständige Datenbanktabelle für diese eine Verwendung zu
erstellen

@HXCaine Ich bin respektvoll anderer Meinung - ich kann mindestens eine andere Verwendung einer SQLite-Tabelle für Benutzer / Passwörter sehen. Wenn Sie das Risiko (bei der Verwendung von SQLite) als akzeptabel betrachten, können Sie in der Tabelle neben der einfachen Anmeldeauthentifizierung für Anwendungen auch mehrere FTP-Kennwörter speichern (wenn Ihre App manchmal FTP verwendet - meine manchmal). Außerdem ist das Erstellen einer SQLite-Adapterklasse für diese Manipulation ganz einfach.
Tony Gil

Schöne Auferstehung eines zwei Jahre alten Kommentars! Um fair zu sein, mein Kommentar war ein Jahr nach der Antwort :) Selbst mit einer Handvoll FTP-Passwörtern ist der Overhead bei einer SQLite-Tabelle sowohl hinsichtlich des Speicherplatzes als auch der Codierung viel größer als bei SharedPreferences. Das kann doch nicht nötig sein
HXCaine
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.