tarjeta studio sobre samsung programacion para móviles moviles libros interno edición desarrollo desarrollar curso como cambiar aprende aplicaciones almacenamiento java android encryption preferences credentials

java - studio - ¿Cuál es la forma más adecuada de almacenar la configuración del usuario en la aplicación de Android?



manual de programacion android pdf (14)

Estoy creando una aplicación que se conecta al servidor con un nombre de usuario / contraseña y me gustaría habilitar la opción "Guardar contraseña" para que el usuario no tenga que escribir la contraseña cada vez que se inicia la aplicación.

Estaba intentando hacerlo con Preferencias Compartidas, pero no estoy seguro de si esta es la mejor solución.

Agradecería cualquier sugerencia sobre cómo almacenar valores / configuraciones de usuario en la aplicación de Android.


Acerca de la forma más sencilla de almacenar una única preferencia en una actividad de Android es hacer algo como esto:

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

Si le preocupa la seguridad de estos, entonces siempre podría cifrar la contraseña antes de almacenarla.


Bueno; ha pasado un tiempo desde que la respuesta es de tipo mixto, pero aquí hay algunas respuestas comunes. Investigué esto como loco y fue difícil construir una buena respuesta

  1. El método MODE_PRIVATE se considera generalmente seguro, si asume que el usuario no arraigó el dispositivo. Sus datos se almacenan en texto sin formato en una parte del sistema de archivos al que solo puede acceder el programa original. Esto hace que sea fácil agarrar la contraseña con otra aplicación en un dispositivo rooteado. Entonces, de nuevo, ¿quieres soportar dispositivos rooteados?

  2. AES sigue siendo el mejor cifrado que puedes hacer. Recuerde buscar esto si está iniciando una nueva implementación si ha pasado un tiempo desde que publiqué esto. El mayor problema con esto es "¿Qué hacer con la clave de cifrado?"

Entonces, ahora estamos en el "¿Qué hacer con la clave?" parte. Esta es la parte difícil. Obtener la llave resulta no ser tan malo. Puedes usar una función de derivación de clave para tomar una contraseña y convertirla en una clave bastante segura. Te metes en cuestiones como "¿cuántos pases haces con PKFDF2?", Pero ese es otro tema

  1. Lo ideal es que guarde la clave AES fuera del dispositivo. Debe encontrar una buena manera de recuperar la clave del servidor de forma segura, confiable y segura.

  2. Tiene una secuencia de inicio de sesión de algún tipo (incluso la secuencia de inicio de sesión original que realiza para el acceso remoto). Puede hacer dos ejecuciones de su generador de claves en la misma contraseña. Cómo funciona esto es que deriva la clave dos veces con un nuevo salt y un nuevo vector de inicialización seguro. Almacena una de esas contraseñas generadas en el dispositivo y usa la segunda contraseña como clave AES.

Cuando inicia sesión, vuelve a derivar la clave en el inicio de sesión local y la compara con la clave almacenada. Una vez hecho esto, utiliza la clave derive # 2 para AES.

  1. Usando el enfoque "generalmente seguro", usted encripta los datos usando AES y almacena la clave en MODE_PRIVATE. Esto es recomendado por una publicación de blog reciente de Android. No increíblemente seguro, pero mucho mejor para algunas personas en texto simple

Puedes hacer muchas variaciones de estos. Por ejemplo, en lugar de una secuencia de inicio de sesión completa, puede hacer un PIN rápido (derivado). Es posible que el PIN rápido no sea tan seguro como una secuencia de inicio de sesión completa, pero es mucho más seguro que el texto simple


En general, las preferencias compartidas son su mejor opción para almacenar preferencias, por lo que, en general, recomiendo este enfoque para guardar la configuración de la aplicación y del usuario.

La única área de preocupación aquí es lo que estás ahorrando. Las contraseñas son siempre una cosa difícil de almacenar, y me preocuparía particularmente almacenarlas como texto claro. La arquitectura de Android es tal que las SharedPreferences de su aplicación están aisladas para evitar que otras aplicaciones puedan acceder a los valores, por lo que existe cierta seguridad allí, pero el acceso físico a un teléfono podría permitir el acceso a los valores.

De ser posible, consideraría modificar el servidor para usar un token negociado para proporcionar acceso, algo como OAuth . Alternativamente, puede que necesite construir algún tipo de almacenamiento criptográfico, aunque eso no es trivial. Como mínimo, asegúrese de cifrar la contraseña antes de escribirla en el disco.


En primer lugar, creo que los datos del usuario no deben almacenarse en el teléfono, y si es necesario almacenar datos en algún lugar del teléfono, se deben cifrar con los datos privados de las aplicaciones. La seguridad de las credenciales de los usuarios debe ser la prioridad de la aplicación.

Los datos confidenciales deben almacenarse de forma segura o en absoluto. En caso de pérdida de un dispositivo o infección de malware, los datos almacenados de forma insegura pueden verse comprometidos.


Esta respuesta se basa en un enfoque sugerido por Mark. Se crea una versión personalizada de la clase EditTextPreference que se convierte entre el texto sin formato que se ve en la vista y una versión encriptada de la contraseña almacenada en el almacenamiento de preferencias.

Como ha señalado la mayoría de los que han respondido en este hilo, esta no es una técnica muy segura, aunque el grado de seguridad depende en parte del código de cifrado / descifrado utilizado. Pero es bastante simple y conveniente, y frustrará la investigación más casual.

Aquí está el código para la clase personalizada EditTextPreference:

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

Esto muestra cómo se puede usar: este es el archivo de "elementos" que impulsa la visualización de preferencias. Tenga en cuenta que contiene tres vistas comunes de EditTextPreference y una de las vistas personalizadas de EditPasswordPreference.

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

En cuanto al cifrado / descifrado real, se deja como un ejercicio para el lector. Actualmente estoy usando un código basado en este artículo http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , aunque con valores diferentes Para la clave y el vector de inicialización.


Estoy de acuerdo con Reto y fiXedd. Hablando objetivamente, no tiene mucho sentido invertir mucho tiempo y esfuerzo para cifrar las contraseñas en SharedPreferences, ya que cualquier atacante que tenga acceso a su archivo de preferencias también tendrá acceso al binario de su aplicación, y por lo tanto las claves para desencriptar el contraseña.

Sin embargo, dicho esto, parece que hay una iniciativa de publicidad en la identificación de aplicaciones móviles que almacenan sus contraseñas en texto claro en SharedPreferences y arroja luz desfavorable sobre esas aplicaciones. Consulte http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ y http://viaforensics.com/appwatchdog para ver algunos ejemplos.

Si bien necesitamos que se preste más atención a la seguridad en general, yo diría que este tipo de atención en este tema en particular no aumenta significativamente nuestra seguridad general. Sin embargo, como las percepciones son como son, aquí hay una solución para cifrar los datos que coloca en SharedPreferences.

Simplemente envuelva su propio objeto SharedPreferences en este, y cualquier dato que lea / escriba se cifrará y descifrará automáticamente. p.ej.

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

Aquí está el código para la clase:

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


Las preferencias compartidas son la forma más fácil de almacenar los datos de nuestra aplicación. pero es posible que cualquiera pueda borrar nuestros datos de preferencias compartidos a través del administrador de aplicaciones. Entonces, no creo que sea completamente seguro para nuestra aplicación.


Sé que esto es un poco de nigromancia, pero debes usar el AccountManager Android. Está diseñado específicamente para este escenario. Es un poco engorroso, pero una de las cosas que hace es invalidar las credenciales locales si la tarjeta SIM cambia, por lo que si alguien desliza su teléfono y lanza una nueva tarjeta SIM, sus credenciales no se verán comprometidas.

Esto también le brinda al usuario una manera rápida y fácil de acceder (y posiblemente eliminar) las credenciales almacenadas para cualquier cuenta que tengan en el dispositivo, todo desde un solo lugar.

SampleSyncAdapter es un ejemplo que hace uso de credenciales de cuenta almacenadas.



Usando el fragmento proporcionado por Richard, puede cifrar la contraseña antes de guardarla. Sin embargo, la API de preferencias no proporciona una manera fácil de interceptar el valor y cifrarlo: puede bloquearlo para que se guarde a través de un agente de escucha OnPreferenceChange, y teóricamente podría modificarlo a través de unChangeListener preferences, pero eso da como resultado un bucle sin fin.

Anteriormente había sugerido agregar una preferencia "oculta" para lograr esto. Definitivamente no es la mejor manera. Voy a presentar otras dos opciones que considero más viables.

Primero, el más simple, está en un ChangeListener preferences, puede tomar el valor ingresado, cifrarlo y luego guardarlo en un archivo de preferencias alternativo:

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 segunda forma, y ​​la forma en que ahora prefiero, es crear su propia preferencia personalizada, extendiendo EditTextPreference, @ Override''ing los métodos setText() y getText() , de modo que setText() cifra la contraseña y getText() devuelve nulo.


Utilizo el KeyStore de Android para cifrar la contraseña usando RSA en modo ECB y luego guardarla en las SharedPreferences.

Cuando quiero recuperar la contraseña, leo el cifrado de las SharedPreferences y lo descifro usando el KeyStore.

Con este método, genera un par de claves públicas / privadas donde el privado es almacenado y administrado de forma segura por Android.

Aquí hay un enlace sobre cómo hacer esto: Tutorial de KeyStore de Android


Voy a lanzar mi sombrero al ring solo para hablar sobre la seguridad de las contraseñas en general en Android. En Android, el dispositivo binario debe considerarse comprometido, es lo mismo para cualquier aplicación final que esté bajo el control directo del usuario. Conceptualmente, un pirata informático podría usar el acceso necesario al binario para descompilarlo y eliminar sus contraseñas cifradas, etc.

Como tal, hay dos sugerencias que me gustaría lanzar si la seguridad es una preocupación importante para usted:

1) No almacene la contraseña real. Almacene un token de acceso otorgado y use el token de acceso y la firma del teléfono para autenticar el lado del servidor de la sesión. El beneficio de esto es que puede hacer que el token tenga una duración limitada, no está comprometiendo la contraseña original y tiene una buena firma que puede usar para correlacionar con el tráfico más adelante (por ejemplo, para verificar intentos de intrusión e invalidar la token haciéndolo inútil).

2) Utilizar la autenticación de 2 factores. Esto puede ser más molesto e intrusivo, pero para algunas situaciones de cumplimiento inevitables.


necesitas usar sqlite, security apit para almacenar las contraseñas. Aquí está el mejor ejemplo, que almacena las contraseñas, - contraseñas seguras. Aquí hay un enlace para la fuente y la explicación: http://code.google.com/p/android-passwordsafe/


Esta es una respuesta complementaria para los que llegan aquí en función del título de la pregunta (como lo hice yo) y no es necesario que se ocupen de los problemas de seguridad relacionados con el ahorro de contraseñas.

Cómo usar las preferencias compartidas

Las configuraciones de usuario generalmente se guardan localmente en Android usando SharedPreferences con un par clave-valor. Utiliza la tecla String para guardar o buscar el valor asociado.

Escribir a las preferencias compartidas

String key = "myInt"; int valueToSave = 10; SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(key, valueToSave).commit();

Use apply() lugar de commit() para guardar en segundo plano en lugar de hacerlo inmediatamente.

Leer de las preferencias compartidas

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

El valor predeterminado se utiliza si no se encuentra la clave.

Notas

  • En lugar de usar una cadena de clave local en varios lugares como lo hice anteriormente, sería mejor usar una constante en una sola ubicación. Puedes usar algo como esto en la parte superior de tu actividad de configuración:

    final static String PREF_MY_INT_KEY = "myInt";

  • Utilicé un int en mi ejemplo, pero también puedes usar putString() , putBoolean() , getString() , getBoolean() , etc.

  • Consulte la documentation para más detalles.
  • Hay varias formas de obtener SharedPreferences. Vea esta respuesta para saber qué buscar.