Je construis une application React Native et j'ai besoin d'enregistrer des données sensibles comme un jeton et un jeton d'actualisation. La solution évidente consiste à enregistrer ces informations à l'aide de AsyncStorage . Le problème est le niveau de sécurité d'AsyncStorage.
AsyncStorage fournit un moyen de stocker localement des jetons et des données. Il peut être, à certains égards, comparé à une option LocalStorage. Dans les applications de production complète, il est recommandé de ne pas accéder directement à AsyncStorage, mais à la place, d'utiliser une couche d'abstraction, car AsyncStorage est partagé avec d'autres applications à l'aide du même navigateur, et donc une suppression mal conçue de tous les éléments du stockage pourrait altérer la fonctionnement des applications voisines.
https://auth0.com/blog/adding-authentication-to-react-native-using-jwt/
Dans une application native, je choisirais Keychain
dans iOS
et Shared Preferences
en mode privé en Android
.
Pour ce que j'ai lu dans la documentation fournie par React Native:
Sur iOS, AsyncStorage est soutenu par un code natif qui stocke de petites valeurs dans un dictionnaire sérialisé et des valeurs plus grandes dans des fichiers séparés. Sur Android, AsyncStorage utilisera soit RocksDB ou SQLite en fonction de ce qui est disponible.
https://facebook.github.io/react-native/docs/asyncstorage.html
Ils ne parlent jamais de la sécurité de ces données.
C'est la meilleure solution pour créer un module pour Android
(qui utilise Shared Preferences
en mode privé ) et un autre pour iOS
(qui utilise Porte-clés ) pour enregistrer les données sensibles? Ou est-il sûr d'utiliser les méthodes AsyncStorage
fournies?
En creusant juste dans le code React Native, j'ai trouvé la réponse.
Android
Le React Native
AsyncStorage
l'implémentation du module est basée sur SQLiteOpenHelper
. Le package où toutes les classes de données sont traitées: https://github.com/facebook/react-native/tree/master/ReactAndroid/src/main/Java/com/facebook/react/modules/storage
La classe avec les instructions pour créer la base de données: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/Java/com/facebook/react/modules/storage/ ReactDatabaseSupplier.Java
Par la documentation Android Android, les bases de données créées par l'application sont enregistrées dans l'espace disque privé de l'application associée, elle est donc sécurisée.
Tout comme les fichiers que vous enregistrez sur le stockage interne de l'appareil, Android stocke votre base de données dans l'espace disque privé de l'application associée. Vos données sont sécurisées, car par défaut cette zone n'est pas accessible aux autres applications.
iOS
Dans iOS, les valeurs AsyncStorage
sont enregistrées dans des fichiers de dictionnaire sérialisés. Ces fichiers sont enregistrés dans l'application NSDocumentDirectory
. Dans iOS, toutes les applications vivent dans leur propre sandbox , donc tous les fichiers d'une application sont sécurisés, ils ne sont pas accessibles par les autres applications.
Le code dans iOS qui gère le module AsyncStorage
peut être trouvé ici: https://github.com/facebook/react-native/blob/master/React/Modules/RCTAsyncLocalStorage.m =
Et comme nous pouvons le voir ici les fichiers utilisés pour stocker les valeurs enregistrées par AsyncStorage
sont enregistrés sous NSDocumentDirectory
(à l'intérieur de l'environnement sandbox de l'application).
Chaque application est une île Les interactions d'une application iOS avec le système de fichiers se limitent principalement aux répertoires dans le bac à sable de l'application. Lors de l'installation d'une nouvelle application, le programme d'installation crée un certain nombre de conteneurs pour l'application. Chaque conteneur a un rôle spécifique. Le conteneur de bundles contient le bundle de l'application, tandis que le conteneur de données contient des données pour l'application et l'utilisateur. Le conteneur de données est en outre divisé en un certain nombre de répertoires que l'application peut utiliser pour trier et organiser ses données. L'application peut également demander l'accès à des conteneurs supplémentaires (par exemple, le conteneur iCloud) lors de l'exécution.
Conclusion
Il est sûr d'utiliser AsyncStorage
pour enregistrer les jetons utilisateur, car ils sont enregistrés dans un contexte sécurisé.
Veuillez noter que cela n'est vrai que pour les appareils Android sans root et pour les appareils iOS sans jailbreak . Veuillez également noter que si l'attaquant a un accès physique à l'appareil et que l'appareil n'est pas Il peut connecter l'appareil à l'ordinateur portable mac et extraire les documents répertoire et voir tous les contenus enregistrés sous le répertoire documents.
AsyncStorage
enregistre les paires clé-valeur sous forme de fichier JSON en texte brut dans le répertoire Documents. Il ne crypte pas son contenu .
Il s'agit d'un problème de sécurité (au moins sur iOS) car il est possible pour un attaquant ayant accès à l'appareil d'obtenir un vidage du contenu du sandbox et d'extraire trivialement toutes les données enregistrées via AsyncStorage
.
Auparavant, cela n'était pas clairement indiqué dans les documents pour AsyncStorage.js, mais c'est maintenant: https://github.com/facebook/react-native/pull/8809
Voir également: https://stackoverflow.com/a/38398114/1072846
Si quelqu'un veut l'étape supplémentaire de cryptage des données, vous voudrez peut-être regarder ceci: https://github.com/oblador/react-native-keychain
Il utilise facebook caché en interne.
Je vous recommande vraiment d'utiliser une bibliothèque comme react-native-keychain pour stocker des données privées dans react-native
Pour Android niveau API:
Vous pouvez l'utiliser comme ça:
// 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);
});