[Java] Quelle est la manière la plus appropriée de stocker les paramètres utilisateur dans l'application Android?



Answers

Je suis d'accord avec Reto et fiXedd. Objectivement parlant, il est inutile 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 a probablement accès au binaire de votre application, et donc aux clés pour le décrypter. mot de passe.

Cependant, cela étant dit, il semble qu'il y ait une initiative publicitaire visant à identifier les applications mobiles qui stockent leurs mots de passe en clair dans SharedPreferences et qui mettent en lumière de façon défavorable 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.

Alors que nous devons 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 de manière significative notre sécurité globale. Cependant, les perceptions étant telles qu'elles sont, voici une solution pour chiffrer 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);
        }
    }

}
Question

Je crée une application qui se connecte au serveur en utilisant le 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 fois que l'application démarre.

J'essayais de le faire avec Shared Preferences mais je ne suis pas sûr que ce soit la meilleure solution.

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







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




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

  1. La méthode MODE_PRIVATE est généralement considérée comme 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. Ce makings saisissant le mot de passe avec une autre application sur un appareil enraciné facile. Là encore, voulez-vous soutenir les appareils enracinés?

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

Donc, maintenant nous sommes à la "Que faire avec la clé?" portion. C'est la partie difficile. Obtenir la clé s'avère être 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 avez des problèmes comme "combien de passages faites-vous avec PKFDF2?", Mais c'est un autre sujet

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

  2. Vous avez une séquence de connexion quelconque (même la séquence de connexion d'origine que vous faites pour l'accès à distance). Vous pouvez faire deux séries de votre générateur de clé 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 en utilisant AES et enregistrez la clé dans MODE_PRIVATE. Ceci est recommandé par un récent article de blog Android. Pas incroyablement sécurisé, mais bien meilleur pour certaines personnes par rapport au texte brut

Vous pouvez faire beaucoup de variations de ceux-ci. 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 peut ne pas être aussi sûr qu'une séquence de connexion complète, mais il est beaucoup plus sécurisé que le texte brut




En utilisant l'extrait fourni par Richard, vous pouvez crypter le mot de passe avant de l'enregistrer. L'API de préférences ne fournit cependant pas un moyen facile d'intercepter la valeur et de la chiffrer - vous pouvez la bloquer en la sauvegardant via un écouteur OnPreferenceChange, et vous pouvez théoriquement la modifier via un PreferChangeListener, mais cela entraîne une boucle infinie.

J'avais précédemment suggéré d'ajouter une préférence "cachée" pour accomplir ceci. Ce n'est certainement pas le meilleur moyen. Je vais présenter deux autres options que je considère plus viables.

Tout d'abord, le plus simple est dans un PreferChangeListener, vous pouvez saisir la valeur entrée, la chiffrer, 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 dont je préfère maintenant, est de créer votre propre préférence personnalisée, en étendant EditTextPreference, @ Remplacer les setText() et getText() , de sorte que setText() chiffre le mot de passe, et getText() renvoie nul.




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 avec 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 d'un périphérique ou d'une infection par un logiciel malveillant, les données stockées de manière non sécurisée peuvent être compromises.




Links