Quelle est la façon la plus appropriée de stocker les paramètres utilisateur dans l'application Android


308

Je crée une application qui se connecte au serveur en utilisant un nom d'utilisateur / mot de passe et je voudrais activer l'option "Enregistrer le mot de passe" pour que l'utilisateur n'ait pas à taper le mot de passe à chaque démarrage de l'application.

J'essayais de le faire avec les préférences partagées mais je ne sais pas si c'est la meilleure solution.

J'apprécierais toute suggestion sur la façon de stocker les valeurs / paramètres utilisateur dans l'application Android.

Réponses:


233

En général, les préférences partagées sont votre meilleur pari pour le stockage des préférences, donc en général, je recommanderais cette approche pour enregistrer les paramètres d'application et d'utilisateur.

Le seul sujet de préoccupation ici est ce que vous économisez. Les mots de passe sont toujours une chose délicate à stocker, et je serais particulièrement prudent de les stocker en texte clair. L'architecture Android est telle que les préférences partagées de votre application sont mises en bac à sable pour empêcher d'autres applications d'accéder aux valeurs, il y a donc une certaine sécurité, mais l'accès physique à un téléphone peut potentiellement permettre l'accès aux valeurs.

Si possible, j'envisagerais de modifier le serveur pour utiliser un jeton négocié pour fournir l'accès, quelque chose comme OAuth . Alternativement, vous devrez peut-être construire une sorte de magasin de chiffrement, bien que ce ne soit pas trivial. À tout le moins, assurez-vous de crypter le mot de passe avant de l'écrire sur le disque.


3
Pourriez-vous expliquer ce que vous entendez par bac à sable?
Abhijit du

14
un programme en bac à sable est une application dont le processus et les informations (telles que les préférences partagées) restent cachées au reste des applications. Une application Android exécutée dans un package ne peut pas accéder directement à quoi que ce soit à l'intérieur d'un autre package. C'est pourquoi les applications dans le même package (qui sont toujours les vôtres) pourraient accéder aux informations des autres
Korcholis

@Reto Meier, mon exigence est de protéger les services Web accessibles au public pour lesquels j'utilise un jeton, le stocker sur des préférences partagées est-il sûr? J'ai un récepteur de diffusion de démarrage dans mon application qui supprimera toutes les données de préférences partagées s'il a trouvé le périphérique enraciné. Est-ce suffisant pour protéger mon jeton.
pyus13

1
Conformément à android-developers.blogspot.com/2013/02/… , les informations d'identification de l'utilisateur doivent être stockées avec l'indicateur MODE_PRIVATE défini et stockées dans la mémoire interne (avec les mêmes mises en garde concernant le stockage de toute sorte de mot de passe localement ouvert à l'attaque). Cela dit, l'utilisation MODE_PRIVATEavec SharedPreferences équivaut-elle à faire de même avec un fichier créé sur le stockage interne, en termes d'efficacité pour obscurcir les données stockées localement?
2013

6
Ne stockez pas de mot de passe dans les préférences partagées. Si l'utilisateur perd le téléphone, il a perdu le mot de passe. Il sera lu. S'ils ont utilisé ce mot de passe ailleurs, partout où ils l'ont utilisé, il est compromis. De plus, vous avez définitivement perdu ce compte car avec le mot de passe, ils peuvent changer votre mot de passe. La bonne façon de procéder consiste à envoyer le mot de passe au serveur une fois et à recevoir un jeton de connexion. Enregistrez cela dans les préférences partagées et envoyez-le à chaque demande. Si ce jeton est compromis, rien d'autre n'est perdu.
Gabe Sechan

211

Je suis d'accord avec Reto et fiXedd. Objectivement parlant, cela n'a pas beaucoup de sens d'investir beaucoup de temps et d'efforts dans le cryptage des mots de passe dans SharedPreferences, car tout attaquant qui a accès à votre fichier de préférences est également susceptible d'avoir également accès au binaire de votre application, et donc les clés pour décrypter le mot de passe.

Cependant, cela étant dit, il semble y avoir une initiative publicitaire visant à identifier les applications mobiles qui stockent leurs mots de passe en texte clair dans SharedPreferences et à éclairer défavorablement ces applications. Voir http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ et http://viaforensics.com/appwatchdog pour quelques exemples.

Bien que nous devions accorder plus d'attention à la sécurité en général, je dirais que ce genre d'attention sur cette question particulière n'augmente pas réellement notre sécurité globale. Cependant, les perceptions étant telles qu'elles sont, voici une solution pour crypter les données que vous placez dans SharedPreferences.

Enveloppez simplement votre propre objet SharedPreferences dans celui-ci, et toutes les données que vous lisez / écrivez seront automatiquement cryptées et décryptées. par exemple.

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);

Voici le code de la classe:

/**
 * 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 est disponible dans l'API niveau 8 (2.2) et versions ultérieures. Vous pouvez utiliser iharder.sourceforge.net/current/java/base64 ou autre chose pour les systèmes d'exploitation antérieurs.
emmby

34
Oui, j'ai écrit ça. N'hésitez pas à utiliser, aucune attribution nécessaire
emmby

8
Je suis d'accord avec toi. Mais si le mot de passe n'est utilisé que sur le serveur, pourquoi ne pas utiliser le cryptage à clé publique / privée? Clé publique sur le client lors de l'enregistrement du mot de passe. Le client n'aura jamais à relire le mot de passe en texte clair, non? Le serveur peut ensuite le déchiffrer avec la clé privée. Donc, même si quelqu'un passe par le code source de votre application, il ne peut pas obtenir le mot de passe, sauf qu'il pirate votre serveur et obtient la clé privée.
Patrick Boos

4
J'ai ajouté quelques fonctionnalités à ce code et je l'ai placé sur github sur github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/… . Il gère désormais la migration des préférences non chiffrées vers les préférences chiffrées. En outre, il génère la clé au moment de l'exécution, donc la décompilation de l'application ne libère pas la clé.
RightHandedMonkey

3
Ajout tardif, mais le commentaire de @PatrickBoos est une excellente idée. Un problème avec cela, cependant, est que même si vous avez chiffré le mot de passe, un attaquant qui a volé ce chiffre pourrait toujours se connecter à vos serveurs, car vos serveurs effectuent le déchiffrement. Un ajout à cette approche consiste à crypter le mot de passe avec un horodatage. De cette façon, vous pouvez décider, par exemple, de n'autoriser que les mots de passe enregistrés dans un passé récent (comme ajouter une date d'expiration à votre "jeton"), ou même exiger que certains utilisateurs aient un horodatage depuis une date particulière (laissez-nous "révoquer" anciens "jetons").
adevine

29

À propos du moyen le plus simple de stocker une seule préférence dans une activité Android, procédez comme suit:

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

Si vous vous inquiétez de leur sécurité, vous pouvez toujours crypter le mot de passe avant de le stocker.


9
Je ne pourrais pas être plus d'accord avec vous sur cette approche simpliste; Cependant, vous devriez toujours vous inquiéter de la sécurité des mots de passe que vous stockez? Selon votre demande, vous avez des responsabilités potentielles pour les informations personnelles volées. Il suffit de le signaler à quiconque essaie de stocker des mots de passe réels vers des choses telles que des comptes bancaires ou quelque chose de tout aussi important. Je vous vote quand même.
While-E

1
Où stockeriez-vous la clé qui a stocké le mot de passe? Si les préférences partagées sont accessibles par d'autres utilisateurs, la clé l'est également.
OrhanC1

@ OrhanC1 avez-vous obtenu la réponse.?
eRaisedToX

10

À l'aide de l'extrait fourni par Richard, vous pouvez crypter le mot de passe avant de l'enregistrer. L'API des préférences ne fournit cependant pas un moyen facile d'intercepter la valeur et de la crypter - vous pouvez bloquer sa sauvegarde via un écouteur OnPreferenceChange, et vous pourriez théoriquement la modifier via un préférenceChangeListener, mais cela se traduit par une boucle sans fin.

J'avais déjà suggéré d'ajouter une préférence "cachée" pour y parvenir. Ce n'est certainement pas la meilleure façon. Je vais présenter deux autres options que je considère comme plus viables.

Tout d'abord, le plus simple, se trouve dans un PreferChangeListener, vous pouvez récupérer la valeur saisie, la crypter, puis l'enregistrer dans un autre fichier de préférences:

  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; 
   }

La deuxième façon, et la façon que je préfère maintenant, est de créer votre propre préférence personnalisée, en étendant EditTextPreference, @ Override'ing les méthodes setText()et getText(), de sorte que setText()crypte le mot de passe et getText()retourne null.


Je sais que c'est assez ancien, mais cela vous dérangerait-il de publier votre code pour votre version personnalisée de EditTextPreference, s'il vous plaît?
RenniePet

Peu importe, j'ai trouvé un échantillon utilisable ici groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M et je le fais fonctionner maintenant. Merci d'avoir suggéré cette approche.
RenniePet

6

D'accord; cela fait un moment que la réponse est plutôt mitigée, mais voici quelques réponses courantes. J'ai recherché cela comme un fou et il était difficile de construire une bonne réponse

  1. La méthode MODE_PRIVATE est considérée comme généralement sûre, si vous supposez que l'utilisateur n'a pas rooté le périphérique. Vos données sont stockées en texte brut dans une partie du système de fichiers accessible uniquement par le programme d'origine. Cela facilite la saisie du mot de passe avec une autre application sur un appareil rooté. Là encore, voulez-vous prendre en charge les appareils enracinés?

  2. AES est toujours le meilleur cryptage possible. N'oubliez pas de rechercher ceci si vous commencez une nouvelle implémentation si cela fait un moment que je n'ai pas posté cela. Le plus gros problème avec ceci est "Que faire avec la clé de cryptage?"

Donc, maintenant, nous sommes au "Que faire avec la clé?" portion. C'est la partie difficile. Obtenir la clé n'est pas si mal. Vous pouvez utiliser une fonction de dérivation de clé pour prendre un mot de passe et en faire une clé assez sécurisée. Vous rencontrez des problèmes comme "combien de passes faites-vous avec PKFDF2?", Mais c'est un autre sujet

  1. Idéalement, vous stockez la clé AES sur l'appareil. Vous devez trouver un bon moyen de récupérer la clé du serveur en toute sécurité, de manière fiable et sécurisée

  2. Vous avez une séquence de connexion quelconque (même la séquence de connexion d'origine que vous effectuez pour l'accès à distance). Vous pouvez effectuer deux exécutions de votre générateur de clés sur le même mot de passe. Comment cela fonctionne est que vous dérivez la clé deux fois avec un nouveau sel et un nouveau vecteur d'initialisation sécurisé. Vous stockez l'un de ces mots de passe générés sur l'appareil et vous utilisez le deuxième mot de passe comme clé AES.

Lorsque vous vous connectez, vous dérivez la clé sur la connexion locale et la comparez à la clé stockée. Une fois cela fait, vous utilisez la clé dérivée # 2 pour AES.

  1. En utilisant l'approche "généralement sûre", vous cryptez les données à l'aide d'AES et stockez la clé dans MODE_PRIVATE. Ceci est recommandé par un article de blog Android récent. Pas incroyablement sécurisé, mais bien meilleur pour certaines personnes que sur du texte brut

Vous pouvez en faire de nombreuses variantes. Par exemple, au lieu d'une séquence de connexion complète, vous pouvez faire un code PIN rapide (dérivé). Le code PIN rapide n'est peut-être pas aussi sécurisé qu'une séquence de connexion complète, mais il est beaucoup plus sûr que le texte brut


5

Je sais que c'est un peu de nécromancie, mais vous devez utiliser le AccountManager Android . Il est spécialement conçu pour ce scénario. C'est un peu lourd mais l'une des choses qu'il fait est d'invalider les informations d'identification locales si la carte SIM change, donc si quelqu'un glisse votre téléphone et y ajoute une nouvelle carte SIM, vos informations d'identification ne seront pas compromises.

Cela donne également à l'utilisateur un moyen rapide et facile d'accéder (et potentiellement de supprimer) les informations d'identification stockées pour tout compte qu'il possède sur l'appareil, le tout à partir d'un seul endroit.

SampleSyncAdapter est un exemple qui utilise les informations d'identification de compte stockées.


1
Veuillez noter que l'utilisation du AccountManager n'est pas plus sécurisée que toute autre méthode fournie ci-dessus! developer.android.com/training/id-auth/…
Sander Versluys

1
Le cas d'utilisation de AccountManager est lorsque le compte doit être partagé entre différentes applications et des applications de différents auteurs. Il ne serait pas approprié de stocker le mot de passe et de le donner à toute application demandeuse. Si l'utilisation de l'utilisateur / mot de passe ne concerne qu'une seule application, n'utilisez pas AccountManager.
dolmen

1
@dolmen, ce n'est pas tout à fait correct. Le AccountManager ne donnera le mot de passe du compte à aucune application dont l'UID ne correspond pas à l'authentificateur. Le nom, oui; le jeton d'authentification, oui; le mot de passe, non. Si vous essayez, cela lèvera une SecurityException. Et le cas d'utilisation est beaucoup plus large que cela. developer.android.com/training/id-auth/identify.html
Jon O

5

Je vais jeter mon chapeau sur le ring juste pour parler de la sécurisation des mots de passe en général sur Android. Sur Android, le binaire de l'appareil doit être considéré comme compromis - c'est la même chose pour toute application finale qui est sous le contrôle direct de l'utilisateur. Conceptuellement, un pirate pourrait utiliser l'accès nécessaire au binaire pour le décompiler et éliminer vos mots de passe cryptés, etc.

En tant que tel, il y a deux suggestions que j'aimerais faire si la sécurité est une préoccupation majeure pour vous:

1) Ne stockez pas le mot de passe réel. Stockez un jeton d'accès accordé et utilisez le jeton d'accès et la signature du téléphone pour authentifier la session côté serveur. L'avantage est que vous pouvez faire en sorte que le jeton ait une durée limitée, que vous ne compromettiez pas le mot de passe d'origine et que vous ayez une bonne signature que vous pouvez utiliser pour établir une corrélation avec le trafic plus tard (pour par exemple vérifier les tentatives d'intrusion et invalider le jeton le rendant inutile).

2) Utilisez l'authentification à 2 facteurs. Cela peut être plus ennuyeux et intrusif, mais pour certaines situations de conformité inévitables.



2

Il s'agit d'une réponse supplémentaire pour ceux qui arrivent ici en fonction du titre de la question (comme je l'ai fait) et n'a pas besoin de traiter les problèmes de sécurité liés à l'enregistrement des mots de passe.

Comment utiliser les préférences partagées

Les paramètres utilisateur sont généralement enregistrés localement dans Android à l'aide SharedPreferencesd'une paire clé-valeur. Vous utilisez la Stringclé pour enregistrer ou rechercher la valeur associée.

Écrire dans les préférences partagées

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

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

Utilisez apply()plutôt que d' commit()enregistrer en arrière-plan plutôt qu'immédiatement.

Lire à partir des préférences partagées

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

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

La valeur par défaut est utilisée si la clé n'est pas trouvée.

Remarques

  • Plutôt que d'utiliser une chaîne de clé locale à plusieurs endroits comme je l'ai fait ci-dessus, il serait préférable d'utiliser une constante dans un seul emplacement. Vous pouvez utiliser quelque chose comme ceci en haut de votre activité de paramètres:

      final static String PREF_MY_INT_KEY = "myInt";
  • J'ai utilisé un intdans mon exemple, mais vous pouvez aussi utiliser putString(), putBoolean(), getString(), getBoolean(), etc.

  • Voir la documentation pour plus de détails.

  • Il existe plusieurs façons d'obtenir des préférences partagées. Voir cette réponse pour savoir quoi rechercher.


1

Cette réponse est basée sur une approche suggérée par Mark. Une version personnalisée de la classe EditTextPreference est créée qui convertit dans les deux sens entre le texte brut vu dans la vue et une version chiffrée du mot de passe stocké dans le stockage des préférences.

Comme cela a été souligné par la plupart des personnes qui ont répondu sur ce sujet, ce n'est pas une technique très sécurisée, bien que le degré de sécurité dépend en partie du code de cryptage / décryptage utilisé. Mais c'est assez simple et pratique, et cela déjouera la plupart des espionnages occasionnels.

Voici le code de la classe EditTextPreference personnalisée:

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);
    }
}

Cela montre comment il peut être utilisé - c'est le fichier "items" qui pilote l'affichage des préférences. Notez qu'il contient trois vues EditTextPreference ordinaires et l'une des vues EditPasswordPreference personnalisées.

<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>

Quant au chiffrement / déchiffrement proprement dit, cela reste un exercice pour le lecteur. J'utilise actuellement du code basé sur cet article http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , bien qu'avec des valeurs différentes pour la clé et le vecteur d'initialisation.


1

Tout d'abord, je pense que les données de l'utilisateur ne doivent pas être stockées sur le téléphone, et s'il est nécessaire de stocker des données quelque part sur le téléphone, elles doivent être cryptées dans les données privées des applications. La sécurité des informations d'identification des utilisateurs doit être la priorité de l'application.

Les données sensibles doivent être stockées en toute sécurité ou pas du tout. En cas de perte de périphérique ou d'infection par un logiciel malveillant, les données stockées de manière non sécurisée peuvent être compromises.


1

J'utilise Android KeyStore pour crypter le mot de passe à l'aide de RSA en mode ECB, puis je l'enregistre dans les SharedPreferences.

Lorsque je veux récupérer le mot de passe, je lis celui chiffré dans SharedPreferences et le déchiffre à l'aide du KeyStore.

Avec cette méthode, vous générez une paire de clés publique / privée où la clé privée est stockée et gérée en toute sécurité par Android.

Voici un lien sur la façon de procéder: Tutoriel Android KeyStore


-2

les préférences partagées sont le moyen le plus simple de stocker nos données d'application. mais il est possible que n'importe qui puisse effacer nos données de préférences partagées via le gestionnaire d'applications. Je ne pense donc pas que cela soit complètement sûr pour notre application.


-3

vous devez utiliser le sqlite, apit de sécurité pour stocker les mots de passe. voici le meilleur exemple, qui stocke les mots de passe, - passwordsafe. voici le lien pour la source et l'explication - http://code.google.com/p/android-passwordsafe/


3
L'OP doit stocker une paire de nom d'utilisateur et de mot de passe. Il serait ridicule d'envisager de créer une table de base de données entière pour cette seule utilisation
HXCaine

@HXCaine je ne suis pas d'accord - je peux voir au moins 1 autre utilisation d'une table sqlite utilisateur / mots de passe. SI VOUS CONSIDÉREZ LE RISQUE (d'utiliser sqlite) ACCEPTABLE, outre la simple authentification de connexion à l'application, vous pouvez utiliser le tableau pour stocker plusieurs mots de passe ftp (si votre application utilise ftp - le mien le fait parfois), par exemple. de plus, la création d'une classe d'adaptateur sqlite pour cette manipulation est simple.
tony gil

Belle résurrection d'un commentaire de deux ans! Pour être honnête, mon commentaire était un an après la réponse :) Même avec une poignée de mots de passe FTP, la surcharge est beaucoup plus grande avec une table SQLite qu'avec SharedPreferences à la fois en termes d'espace et de codage. Cela ne peut sûrement pas être nécessaire
HXCaine
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.