Cómo proteger las preferencias compartidas de Android?
La ubicación común en la que SharedPreferences
se almacenan en las aplicaciones de Android es:
/data/data/<package name>/shared_prefs/<filename.xml>
El usuario con los privilegios de la raíz puede navegar a esta localización y puede cambiar sus valores. La necesidad de protegerla es de mucha importancia.
- Evitar el "cliente falso" para la aplicación ios
- Almacenar el secreto del cliente de forma segura
- Ofuscación: ocultar valores codificados en java
- Pregunta sobre la seguridad del depurador
- Cómo proteger url webservice en el archivo android apk
¿De cuántas maneras podemos cifrar shared_pref's xml
archivo shared_pref's xml
completo de shared_pref's xml
?
Todos sabemos que podemos cifrar y guardar datos en shared_pref's xml
archivo shared_pref's xml
, pero eso no sólo es 100% seguro, así que necesita cifrar todo el archivo con una clave. Necesita ayuda para conocer varias formas de cifrar todo el archivo xml
. Esta es una pregunta genérica, varios métodos de cifrado discutidos como respuestas aquí pueden ser útiles para todos los desarrolladores en la seguridad de las aplicaciones.
- Instalación / Acceso a Certs para VPN / WIFI mediante programación en Android
- Android: el resultado de AsyncTask no se devuelve a la actividad principal
- Uso de un certificado autofirmado para crear una conexión cliente-servidor segura en android
- Android - Almacenamiento de datos confidenciales en la base de datos sqlite
- ¿El almacenamiento de la sal junto con el archivo cifrado romper la seguridad?
- Android Central Keystore
- ¿Qué tan seguras son las API de GeoLocation en dispositivos móviles?
- Cómo Hash String usando SHA-1 con la clave?
Debe tener en cuenta que las preferencias compartidas de Android son basadas en valores clave XML. Usted no puede cambiar ese hecho (ya que rompería su analizador), en el mejor de los casos puede cifrar tanto la clave como el valor, de modo que el usuario root pueda leer, pero no tendrá la menor idea de lo que está leyendo.
Para hacer eso, podría utilizar un cifrado simple como este
public static String encrypt(String input) { // This is base64 encoding, which is not an encryption return Base64.encodeToString(input.getBytes(), Base64.DEFAULT); } public static String decrypt(String input) { return new String(Base64.decode(input, Base64.DEFAULT)); }
Así es como usaría esto
// Write SharedPreferences preferences = getSharedPreferences("some_prefs_name", MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putString(encrypt("password"), encrypt("dummypass")); editor.apply(); // Or commit if targeting old devices // Read SharedPreferences preferences = getSharedPreferences("some_prefs_name", MODE_PRIVATE); String passEncrypted = preferences.getString(encrypt("password"), encrypt("default")); String pass = decrypt(passEncrypted);
Usted debe saber duro, que SharedPreferences
nunca fueron construidos para ser seguro, es sólo una forma sencilla de persistir los datos.
Usted debe ser consciente también de que el cifrado que he utilizado no es el más seguro, pero es simple.
Hay varias bibliotecas que proporcionan mejor cifrado, como estos
- https://github.com/scottyab/secure-preferences
- https://github.com/sveinungkb/encrypted-userprefs
- https://github.com/kovmarci86/android-secure-preferences
- http://www.righthandedmonkey.com/2014/04/obscured-shared-preferences-for-android.html
Pero todos ellos vienen al hecho de que el formato del archivo sigue siendo XML y es clave-valor basado. No puedes cambiar ese hecho. Vea abajo.
cat /data/data/your.package.application/shared_prefs/prefs-test.xml <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="JopRH053b7Ogw17Yxmh7Og==">0AB7Y28XEvbQcnXpEZ4j9PtqzFLtm2V3KBXjTO1V704=</string> </map> The key is "hemmelighet" and the value is "dette er en hemmelighet".
Si la seguridad es un problema más allá del hecho de que SharedPreferences
sigue estando basado en valores clave y en formato XML, debe evitarlo por completo.
public class NodeCrypto { private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!) private IvParameterSpec ivspec; private SecretKeySpec keyspec; private Cipher cipher; private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!) public void doKey(String key) { ivspec = new IvParameterSpec(iv.getBytes()); key = padRight(key,16); Log.d("hi",key); keyspec = new SecretKeySpec(key.getBytes(), "AES"); try { cipher = Cipher.getInstance("AES/CBC/NoPadding"); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchPaddingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public byte[] encrypt(String text,String key) throws Exception { if(text == null || text.length() == 0) throw new Exception("Empty string"); doKey(key); byte[] encrypted = null; try { cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); encrypted = cipher.doFinal(padString(text).getBytes()); } catch (Exception e) { throw new Exception("[encrypt] " + e.getMessage()); } return encrypted; } public byte[] decrypt(String code,String key) throws Exception { if(code == null || code.length() == 0) throw new Exception("Empty string"); byte[] decrypted = null; doKey(key); try { cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); decrypted = cipher.doFinal(hexToBytes(code)); } catch (Exception e) { throw new Exception("[decrypt] " + e.getMessage()); } return decrypted; } public static String bytesToHex(byte[] data) { if (data==null) { return null; } int len = data.length; String str = ""; for (int i=0; i<len; i++) { if ((data[i]&0xFF)<16) str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF); else str = str + java.lang.Integer.toHexString(data[i]&0xFF); } return str; } public static byte[] hexToBytes(String str) { if (str==null) { return null; } else if (str.length() < 2) { return null; } else { int len = str.length() / 2; byte[] buffer = new byte[len]; for (int i=0; i<len; i++) { buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16); } return buffer; } } private static String padString(String source) { char paddingChar = ' '; int size = 16; int x = source.length() % size; int padLength = size - x; for (int i = 0; i < padLength; i++) { source += paddingChar; } return source; } public static String padRight(String s, int n) { return String.format("%1$-" + n + "s", s); } } ----------------------------------------------- from your activity or class call encrypt or decrypt method before saving or retriving from SharedPreference
- Cómo dividir en neón intrínseca por un número de flotador
- Errores de administración del ciclo de vida de UnityPlayer en una aplicación nativa de Android