What is the most appropriate way to store user settings in Android application

后端 未结 14 2084
隐瞒了意图╮
隐瞒了意图╮ 2020-11-22 05:38

I am creating an application which connects to the server using username/password and I would like to enable the option \"Save password\" so the user wouldn\'t have to type

14条回答
  •  粉色の甜心
    2020-11-22 06:03

    I agree with Reto and fiXedd. Objectively speaking it doesn't make a lot of sense investing significant time and effort into encrypting passwords in SharedPreferences since any attacker that has access to your preferences file is fairly likely to also have access to your application's binary, and therefore the keys to unencrypt the password.

    However, that being said, there does seem to be a publicity initiative going on identifying mobile applications that store their passwords in cleartext in SharedPreferences and shining unfavorable light on those applications. See http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ and http://viaforensics.com/appwatchdog for some examples.

    While we need more attention paid to security in general, I would argue that this sort of attention on this one particular issue doesn't actually significantly increase our overall security. However, perceptions being as they are, here's a solution to encrypt the data you place in SharedPreferences.

    Simply wrap your own SharedPreferences object in this one, and any data you read/write will be automatically encrypted and decrypted. eg.

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

    Here's the code for the class:

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

提交回复
热议问题