Guarde los datos confidenciales en React Native

Estoy construyendo una aplicación React Native y necesito guardar algunos datos confidenciales como un token y un token de actualización. La solución obvia es guardar esa información usando AsyncStorage . El problema es el nivel de seguridad de AsyncStorage.

AsyncStorage proporciona una forma de almacenar localmente tokens y datos. Puede ser, de alguna manera, comparado con una opción LocalStorage. En aplicaciones de producción completa, se recomienda no acceder directamente a AsyncStorage, sino utilizar una capa de abstracción, ya que AsyncStorage se comparte con otras aplicaciones que utilizan el mismo navegador y, por lo tanto, una eliminación mal concebida de todos los elementos del almacenamiento podría perjudicar la funcionamiento de aplicaciones vecinas.

https://auth0.com/blog/adding-authentication-to-react-native-using-jwt/

En una aplicación nativa, elegiría Keychain en iOS y Shared Preferences en modo privado en Android .

Por lo que leí en la documentación provista por React Native:

En iOS, AsyncStorage está respaldado por código nativo que almacena valores pequeños en un diccionario serializado y valores más grandes en archivos separados. En Android, AsyncStorage usará RocksDB o SQLite en función de lo que esté disponible.

https://facebook.github.io/react-native/docs/asyncstorage.html

Nunca hablan sobre la seguridad de esos datos.

Es la mejor solución para crear un módulo para Android (que usa Shared Preferences en modo privado ) y otro para iOS (que usa Keychain ) para guardar los datos sensibles. ¿O es seguro utilizar los métodos AsyncStorage proporcionados?

    Solo profundizando en el código React Native, encontré la respuesta.

    Androide

    La implementación del módulo React Native AsyncStorage se basa en SQLiteOpenHelper . El paquete donde se manejan todas las clases de datos: https://github.com/facebook/react-native/tree/master/ReactAndroid/src/main/java/com/facebook/react/modules/storage

    La clase con las instrucciones para crear la base de datos: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/modules/storage/ReactDatabaseSupplier.java

    Según la documentación de Android, las bases de datos creadas por la aplicación se guardan en un espacio de disco privado asociado a la aplicación, por lo que es seguro.

    Al igual que los archivos que guarda en el almacenamiento interno del dispositivo, Android almacena su base de datos en el espacio de disco privado de esa aplicación asociada. Sus datos son seguros porque, de forma predeterminada, otras aplicaciones no pueden acceder a este área.

    Fuente

    iOS

    En iOS, los valores de AsyncStorage se guardan en archivos de diccionario serializados. Esos archivos se guardan en la aplicación NSDocumentDirectory . En iOS, todas las aplicaciones viven en su propio entorno limitado , por lo que todos los archivos de una aplicación están protegidos, y las demás aplicaciones no pueden acceder a ellos.

    El código en iOS que maneja el módulo AsyncStorage se puede encontrar aquí: https://github.com/facebook/react-native/blob/master/React/Modules/RCTAsyncLocalStorage.m

    Y como podemos ver aquí, los archivos utilizados para almacenar los valores guardados por AsyncStorage se guardan en NSDocumentDirectory (dentro del entorno de sandbox de la aplicación).

    Cada aplicación es una isla Las interacciones de una aplicación de iOS con el sistema de archivos están limitadas principalmente a los directorios dentro de la zona de pruebas de la aplicación. Durante la instalación de una nueva aplicación, el instalador crea una cantidad de contenedores para la aplicación. Cada contenedor tiene un rol específico. El contenedor del paquete contiene el paquete de la aplicación, mientras que el contenedor de datos contiene datos tanto para la aplicación como para el usuario. El contenedor de datos se divide además en una serie de directorios que la aplicación puede usar para ordenar y organizar sus datos. La aplicación también puede solicitar el acceso a contenedores adicionales, por ejemplo, el contenedor de iCloud, en tiempo de ejecución.

    Fuente

    Conclusión

    Es seguro utilizar AsyncStorage para guardar tokens de usuario, ya que se guardan en un contexto seguro.

    Tenga en cuenta que esto solo es cierto para dispositivos Android sin raíz y para dispositivos iOS sin jailbreak . Tenga en cuenta también que si el atacante tiene acceso físico al dispositivo y el dispositivo no está protegido. Puede conectar el dispositivo a la computadora portátil Mac y extraer el directorio de documentos y ver todos los contenidos guardados en el directorio de documentos.

    AsyncStorage guarda los pares clave-valor como un archivo JSON de texto plano en el directorio de Documentos. No cifra sus contenidos .

    Este es un problema de seguridad (al menos en iOS) porque es posible que un atacante con acceso al dispositivo obtenga un volcado del contenido de la zona de pruebas y extraiga trivialmente cualquier información guardada a través de AsyncStorage .

    Esto solía no estar claramente indicado en los documentos para AsyncStorage.js, pero ahora es: https://github.com/facebook/react-native/pull/8809

    También vea: https://stackoverflow.com/a/38398114/1072846

    Si alguien quiere el paso adicional de cifrar los datos, es posible que desee ver esto: https://github.com/oblador/react-native-keychain

    Utiliza Facebook ocultar internamente.

    Realmente te recomiendo usar una biblioteca como reactjsr-native-keychain para almacenar datos privados en react-native

    Para el nivel de la API de Android:

    • 16-22 usa Facebook Conceal
    • 23+ usan Android Keystore

    Puedes usarlo así:

     // Generic Password, service argument optional Keychain .setGenericPassword(username, password) .then(function() { console.log('Credentials saved successfully!'); }); // service argument optional Keychain .getGenericPassword() .then(function(credentials) { console.log('Credentials successfully loaded for user ' + credentials.username); }).catch(function(error) { console.log('Keychain couldn\'t be accessed! Maybe no value set?', error); });