SQLCipher para Android getReadableDatabase () Sobrehéroe
He modificado mi clase DatabaseHelper para usar la biblioteca SQLCipher.
Para hacer eso, yo:
- Consulta SQLite en Android para contar filas
- ¿El uso del texto como clave principal en la tabla SQLite es incorrecto?
- Android NDK construir de spatialite?
- Cómo proteger una base de datos sqlite pre hecho en android?
- ¿Cuántas filas puedo insertar en una tabla en SQLite Android?
-
Copiado los activos en mi carpeta de activos y las bibliotecas (armeabi, x86, commons-codec, guava-r09, sqlcipher) en mi carpeta libs.
-
Cambió las importaciones en mi clase
DatabaseHelper
para que apunten aimport net.sqlcipher.database.*
lugar. -
Llame a
SQLiteDatabase.loadLibs(getApplicationContext());
Cuando se inicia la aplicación. -
Modificado las líneas donde llamo
getReadableDatabase()
ygetWriteableDatabase()
para que incluyan una frase de contraseña como un parámetro;
Todo parece funcionar bien, ya que los datos se leen / escriben correctamente. Mi problema está relacionado con el rendimiento, ya que mi aplicación puede ejecutar operaciones de base de datos con cierta frecuencia, haciendo que se vuelva lenta (después de migrar a SQLCipher).
Para mis métodos DatabaseHelper, creo que estoy siguiendo el enfoque estándar, por ejemplo:
/* * Getting all MyObjects */ public List<MyObject> getMyObjects() { List<MyObject> objects = new ArrayList<MyObject>(); String selectQuery = "SELECT * FROM " + TABLE_NAME; Log.v(LOG, selectQuery); // Open SQLiteDatabase db = this.getReadableDatabase("...the password..."); // I know this passphrase can be figured out by decompiling. // Cursor with query Cursor c = db.rawQuery(selectQuery, null); // looping through all rows and adding to list if (c.moveToFirst()) { do { MyObject object = createMyObjectFromCursor(c); // Method that builds MyObject from Cursor data // adding to list objects.add(object); } while (c.moveToNext()); } c.close(); db.close(); return objects; }
No estoy totalmente familiarizado con la mecánica interna de SQLCipher (por ejemplo, ¿descifra todo el archivo DB cuando llamo a getReadableDatabase()
?) Pero, al depurar, parece que la sobrecarga está en getReadableDatabase(password)
y getWritableDatabase(password)
, Lo cual tiene sentido si mi suposición anterior es verdadera.
¿Cambiaría esas llamadas a un método DatabaseHelper.open () y DatabaseHelper.close () que serían llamados por las Actividades cada vez que instancien un DatabaseHelper, en lugar de llamarlos a cada método individual, sería una mala práctica? Por favor comparta sus conocimientos sobre cómo abordar este problema.
EDITAR:
He utilizado DDMS para rastrear uno de los métodos y puedo ver que la sobrecarga es de hecho en el SQLiteOpenHelper.getReadableDatabase()
(tomar ~ 4 seg. Cada vez). Las consultas parecen trabajar rápido y no creo que tenga que preocuparse por ellos.
Si profundizo las llamadas, siguiendo la que tiene la duración más larga cada vez, termino con:
SQLiteDatabase.OpenOrCreateDatabase
-> SqLiteDatabase.openDatabase
-> SQLiteDatabase.openDatabase
-> SQLiteDatabase.setLocale
Así que el SQLiteDatabase.setLocale(java.util.Locale)
parece ser el culpable, ya que está tomando ~ 4 segundos cada vez getReadableDatabase () se llama. He mirado en el origen de SQLiteDatabase y sólo bloquea el DB, llama native_setLocale(locale.toString(), mFlags)
(la 4 sec. Overhead se lleva a cabo aquí) y desbloquea el DB.
¿Alguna idea de por qué sucede esto?
- SelectionArgs in ContentResolver.query (...) puede ser una subconsulta?
- GreenDAO - clave primaria en varias columnas
- Error al azar de SQLiteConnectionPool en Android. ¿Como evitar?
- Java.sql.SQLException: No se puede ejecutar insert stmt en objeto
- Obtener base de datos SQLite desde la aplicación de Android
- No puedo crear una base de datos SQLite en mi aplicación android
- Hacer inserciones SQLite eficientes con Android
- Android - ¿Dónde está la base de datos SQLite almacenada
El problema de rendimiento que está viendo es más probable debido a la derivación de clave SQLCipher. El funcionamiento de SQLCipher para abrir una base de datos es deliberadamente lento, utilizando PBKDF2 para realizar la derivación clave (es decir, miles de operaciones SHA1) para defenderse de los ataques de fuerza bruta y de diccionario (puede leer más sobre esto en http://sqlcipher.net/design ). Esta actividad se aplazó hasta el primer uso de la base de datos, lo que ocurre en setLocale, por lo que está viendo el problema de rendimiento allí al crear perfiles.
La mejor opción es almacenar en caché la conexión de la base de datos de modo que pueda ser utilizada varias veces sin tener que abrir y llave la base de datos repetidamente. Si esto es posible, abrir la base de datos una vez durante el inicio es el curso de acción preferido. El acceso subsiguiente en el mismo identificador de base de datos no activará la derivación clave, por lo que el rendimiento será mucho más rápido.
Si esto no es posible, la otra opción es desactivar o debilitar la derivación clave. Esto hará que SQLCipher utilice menos rondas de PBKDF2 al derivar la clave. Si bien esto hará que la base de datos abra más rápido, es significativamente más débil desde una perspectiva de seguridad. Por lo tanto, no se recomienda excepto en casos excepcionales. Dicho esto, aquí está la información sobre cómo reducir las iteraciones de KDF:
¿Desencripta el archivo DB entero cuando llamo a getReadableDatabase ()?
No. Descifra páginas (4KB ??) según sea necesario, sobre la marcha.
Parece que la sobrecarga está en getReadableDatabase (contraseña) y getWritableDatabase (contraseña), lo cual tiene sentido si mi suposición anterior es verdadera
Sólo llame a aquellos una vez para toda la vida de su proceso. Cualquier otra cosa es insegura, ya que requiere que usted mantenga la contraseña alrededor, por encima y más allá de los problemas generales.
Por supuesto, usted parece ser difícil de codificar una contraseña, en cuyo caso todo este cifrado es inútil y una pérdida de tiempo.
Por favor comparta sus conocimientos sobre cómo abordar este problema.
Utilice Traceview para determinar exactamente dónde se gasta su tiempo.
En un punto de referencia que realicé, convirtiendo un benchmark de SQLite en SQLCipher, no pude detectar ninguna sobrecarga de material. E / S de disco inundó la sobrecarga de cifrado, cerca como puedo decir.
En la medida en que un SQLCipher bien escrito para la aplicación de Android aumente la sobrecarga, empeorará las malas operaciones. Por lo tanto, por ejemplo, una consulta que necesita hacer una exploración de tabla ya se apesta; SQLCipher hará que la succión incrementalmente más difícil. La solución es agregar los índices apropiados (o FTS3) según sea necesario para evitar el escaneo de tablas.
- ¿Cómo probar las notificaciones de GCM sin la aplicación?
- AlertDialog con EditText con el correo electrónico InputType – Android