Cifrado de archivos y preferencias compartidas en Android + Kotlin | de Tom Colvin | febrero 2023

Foto de Akhilesh Sharma Como ya hemos visto, los datos que su aplicación almacena en el almacenamiento externo pueden ser leídos por otras aplicaciones si tienen los permisos adecuados. Por lo tanto, el cifrado adicional es esencial para los datos privados.¹ Los datos almacenados en el almacenamiento interno están mejor protegidos por el almacenamiento perimetral. Pero incluso eso puede no ser del todo seguro. Por ejemplo, los dispositivos rooteados podrían eludir la protección habitual de Android y hacer que los datos internos estén disponibles para otras aplicaciones². También se pueden utilizar sistemas de respaldo o de transmisión. Por tanto, en ambos casos tiene sentido utilizar un nivel de cifrado diferente para datos especialmente sensibles, aunque el propio Android también cifra su sistema de archivos a un nivel bajo. De ahí este artículo. le muestra cómo escribir archivos cifrados y SharedPreferences cifrados usando Kotlin + Compose + ViewModels.

El código de la aplicación de muestra que se presenta aquí está disponible en mi GitHub.

La forma más fácil de implementar la seguridad de archivos es usar la biblioteca de seguridad de Jetpack. Instálelo a través de Gradle:implementation(«androidx.security:security-crypto:1.0.0») de la siguiente manera Antes de realizar cualquier operación de encriptación, lo primero que necesitamos es una clave de encriptación. Jetpack Security hace que esto sea increíblemente fácil (confía en mí, el código a continuación oculta un grande Cantidad de complejidad): Esto va en ViewModel; Lo convertí en una variable perezosa, por lo que solo se crea en el primer uso. El objeto EncryptedSharedPreferences de Jetpack Security es un reemplazo casi directo para las SharedPreferences estándar de Android. Aquí he usado la creación de instancias diferidas, por lo que solo se crea cuando es necesario: tenga en cuenta que debido a que este es un AndroidViewModel, getApplication() (que nos brinda nuestro objeto de aplicación global) se proporciona automáticamente. Escribir en EncryptedSharedPreferences es como SharedPreferences: aquí establecemos la clave de prueba en el valor dado.Del mismo modo, leer EncryptedSharedPreferences es lo mismo que para SharedPreferences: el segundo argumento de getString es el valor predeterminado que se devolverá si la clave no existe. No es posible leer y escribir archivos cifrados Esto no es mucho más complejo que leer y escribir SharedPreferences cifradas. Usando la clave maestra generada anteriormente, podemos crear nuestro objeto EncryptedFile de la siguiente manera: Esto denota un EncryptedFile en el almacenamiento interno de nuestra aplicación (por ejemplo, Application.filesDir) y está encriptado usando AES-256. Luego podemos escribir en ese archivo usando EncryptedFile.openFileOutput() y con una sintaxis muy similar a la antigua Java File API: observe cómo este código usa la función de extensión .us e { } de Kotlin para garantizar que el archivo que abrimos se cierre correctamente incluso si se lanza una excepción. ¡Qué bueno es eso! Me encanta Kotlin. La lectura del archivo también es sencilla con EncryptedFile.openFileInput() y es muy similar a File API: cada desarrollador tiene su propia forma de manejar las lecturas de archivos y la suya puede variar. Tenga en cuenta que el código anterior solo lee los primeros 32 KB del archivo. Para el código de producción, también debe asegurarse de que las operaciones de lectura no detengan el hilo principal. Tenga en cuenta que llamar a EncryptedFile.openFileInput() arroja una IOException si el archivo no existe. (Por cierto, ¿se dio cuenta de cómo pudimos crear numBytesRead como un valor y proporcionar su valor más tarde? Nuevamente, eso es excelente para Kotlin. En muchos otros idiomas, numBytesRead tendría que ser una variable y obtener un valor inicial). Las EncryptedSharedPreferences creadas arriba del archivo se ven así:El archivo EncryptedSharedPreferences, leído directamente desde el disco. Como puede ver, tanto la clave como el valor que escribimos en el archivo están encriptados. Por lo tanto, ni siquiera es posible que un atacante vea qué claves tienen valores, y mucho menos cuáles son esos valores. El archivo cifrado que escribimos es completamente ilegible sin descifrarlo:El EncryptedFile es completamente ilegible cuando se lee directamente desde el disco. En su mayor parte, agregar cifrado con EncryptedFile y EncryptedSharedPreferences es un reemplazo directo y fácil para las API File y SharedPreferences. Sin embargo, hay algunas advertencias:

  • No tiene sentido hacer una copia de seguridad de los archivos cifrados. La clave de cifrado se almacena en el hardware del teléfono. Entonces, cuando restaura un teléfono desde una copia de seguridad, la clave correspondiente no está allí.
  • Si bien esto protege los datos del acceso de root, la clave se almacena de manera que las aplicaciones rooteadas puedan acceder a ella.
  • Obviamente, los gastos generales de cifrado y descifrado significan que las lecturas y escrituras son más lentas. En mi experiencia, la diferencia es imperceptible a menos que esté escribiendo grandes cantidades de datos.

Aparte de eso, no hay razón para no usar el cifrado, y es tan simple como el anterior.

El código de esta aplicación de muestra está en mi GitHub.

Tom Colvin es CTO de Apptaura, los especialistas en desarrollo de aplicaciones; y Conseal Security, los expertos en pruebas de seguridad de aplicaciones móviles. ¡Comuníquese si puedo ayudar con cualquier proyecto de desarrollo o seguridad móvil!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.