Generalidades

Seguridad de datos en la base de datos de habitaciones de Android | Por Elias Eguizabal | Ene 2022

Elías Eguizábal

La mayoría de las aplicaciones utilizan una base de datos local para almacenar la información necesaria para el almacenamiento en caché de datos o para almacenar credenciales, lo que a veces es confidencial porque incluye datos de usuario, credenciales de autenticación y no está debidamente protegido.Para resolver este problema en Android podemos usar la biblioteca contraseña SQL Esto nos ayuda a cifrar la base de datos SQL sin formato y es compatible con Room Android.

Uno de los métodos más populares.UdsLos datos confidenciales se cifran durante el proceso de codificación de la información que convierte el texto sin formato legible por humanos en texto incomprensible. El cifrado requiere el uso de una clave de cifrado, que es una cadena que se utiliza para alterar los datos para que parezcan aleatorios.

SQLCipher es una biblioteca de código abierto que proporciona cifrado AES transparente y seguro de 256 bits para archivos de base de datos SQLite y una fácil integración con aplicaciones Java y Kotlin.

Agregue la dependencia de cifrado en su aplicación build.gradle o el módulo donde implementará la capa de datos.

implementation "net.zetetic:android-database-sqlcipher:4.5.0"
implementation "androidx.security:security-crypto:1.1.0-alpha03"

Reemplace la versión 4.5.0 con El más nuevo

Los constructores de habitaciones más antiguos podrían verse así

Room.databaseBuilder(
context,
DataBase::class.java,
DATABASE_NAME
).build()

ahora tengo una contraseña

Room.databaseBuilder(
context,
DataBase::class.java,
DATABASE_NAME
).openHelperFactory(SupportFactory(securePassphrase)).build()

Código de seguridad Básicamente, su clave en formato ByteArray. Puede obtener esta contraseña de diferentes maneras.

De una cadena simple:

"secureKey".toByteArray()

Cadena decodificada de base64:

Base64.decode("secureKey", Base64.DEFAULT)

De un número aleatorio seguro:

private fun generatePassphrase(): ByteArray {
// Generate a 256-bit key
val outputKeyLength = keyLength

val secureRandom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
SecureRandom.getInstanceStrong()
} else {
// Do *not* seed secureRandom! Automatically seeded from system entropy.
SecureRandom()
}
val keyGenerator: KeyGenerator = KeyGenerator.getInstance(algorithm)
keyGenerator.init(outputKeyLength, secureRandom)

var tempArray = keyGenerator.generateKey().encoded

// filter out zero byte values, as SQLCipher does not like them
while (tempArray.contains(0)) {
tempArray = keyGenerator.generateKey().encoded
}

return tempArray
}

En este proceso, usamos SecureRandom para producir resultados no deterministas, por lo que cualquier material inicial que se pase al objeto SecureRandom debe ser impredecible; este enfoque sería una buena práctica, ya que cada usuario tiene una clave de contraseña diferente. En Android 26 y versiones posteriores, getInstanceStrong se puede usar para obtener valores más fuertes.

Todas estas frases de contraseña deben almacenarse porque necesita usar la misma clave para descifrar los datos cada vez, y la opción perfecta para el almacenamiento es el archivo seguro de la biblioteca de seguridad de Android.

fun getPassphraseFromSecureFile(): ByteArray {
val file = File(context.filesDir, fileName)
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val encryptedFile = EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

return if (file.exists()) {
encryptedFile.openFileInput().use { it.readBytes() }
} else {
generatePassphrase().also { passphrase ->
encryptedFile.openFileOutput().use { it.write(passphrase) }
}
}
}

En este punto, el cifrado está listo, pero en algunos casos necesita migrar su base de datos existente insegura a este nuevo método.

Para facilitar la migración, podemos usar este CipherHelper CWAC-Sala Segura biblioteca y se utiliza para cifrar bases de datos no seguras.

import android.content.Context
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import net.sqlcipher.database.SQLiteDatabase

object SQLCipherUtils {

/**
* Replaces this database with a version encrypted with the supplied
* passphrase, deleting the original. Do not call this while the database
* is open, which includes during any Room migrations.
*
* The passphrase is untouched in this call. If you are going to turn around
* and use it with SafeHelperFactory.fromUser(), fromUser() will clear the
* passphrase. If not, please set all bytes of the passphrase to 0 or something
* to clear out the passphrase.
*
*
@param ctxt a Context
*
@param originalFile a File pointing to the database
*
@param passphrase the passphrase from the user
*
@throws IOException
*/
@Throws(IOException::class)
fun encrypt(ctxt: Context, originalFile: File, passphrase: ByteArray?) {
SQLiteDatabase.loadLibs(ctxt)
if (originalFile.exists()) {
val newFile = File.createTempFile(
"sqlcipherutils", "tmp",
ctxt.cacheDir
)
var db = SQLiteDatabase.openDatabase(
originalFile.absolutePath,
"", null, SQLiteDatabase.OPEN_READWRITE
)

val version = db.version
db.close()
db = SQLiteDatabase.openDatabase(
newFile.absolutePath, passphrase,
null, SQLiteDatabase.OPEN_READWRITE, null, null
)
val st = db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''")
st.bindString(1, originalFile.absolutePath)
st.execute()
db.rawExecSQL("SELECT sqlcipher_export('main', 'plaintext')")
db.rawExecSQL("DETACH DATABASE plaintext")
db.version = version
st.close()
db.close()
originalFile.delete()
newFile.renameTo(originalFile)
} else {
throw FileNotFoundException(originalFile.absolutePath + " not found")
}
}
}

Luego implemente el método de migración en el generador de habitaciones, este es un ejemplo, dependiendo de la implementación de su habitación, tal vez en un módulo singleton o de inyección de dependencia, etc.

val state = SQLCipherUtils.getDatabaseState(
context,
DATABASE_NAME
)

val key = getPassphraseFromSecureFile()

if (state == SQLCipherUtils.State.UNENCRYPTED) {

SQLCipherUtils.encrypt(
context,
DATABASE_NAME,
key
)
}

return Room.databaseBuilder(context, DataBase::class.java, DATABASE_NAME)
.fallbackToDestructiveMigration()
.openHelperFactory(KeyManagerRepository(context).getCypherFactory())
.build()

Se trata de proteger la base de datos de su sala.

LEER  Google quiere asegurarse de que los dispositivos domésticos inteligentes estén listos

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba