72

I am building a React Native application and I need to save some sensitive data like a token and a refresh token. The obvious solution is to save that information using AsyncStorage. The problem is the security level of the AsyncStorage.

AsyncStorage provides a way to locally store tokens and data. It can be, in some ways, compared to a LocalStorage option. In full production applications, it is recommended to not access AsyncStorage directly, but instead, to use an abstraction layer, as AsyncStorage is shared with other apps using the same browser, and thus an ill-conceieved removal of all items from storage could impair the functioning of neighboring apps.

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

In a native app, I would go for Keychain in iOS and Shared Preferences in private mode in Android.

For what I read in the documentation provided by React Native:

On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files. On Android, AsyncStorage will use either RocksDB or SQLite based on what is available.

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

They never talk about the security of that data.

It is the best solution create a module for Android (that uses Shared Preferences in private mode) and another for iOS (that uses Keychain) to save the sensible data? Or it is safe to use the AsyncStorage methods provided?

Sandro Machado
  • 8,485
  • 3
  • 31
  • 54
  • 1
    What you looking for, is answered in this post: http://stackoverflow.com/questions/37563224/why-the-react-native-docs-recommends-that-you-use-an-abstraction-on-top-of-async – Pedro Gomes Aug 23 '16 at 09:59
  • 1
    That answer does not make any assumption regarding the security of the `AsyncStorage`. – Sandro Machado Aug 23 '16 at 10:53
  • You can take a look at this post: https://stackoverflow.com/questions/45547657/what-is-the-best-way-to-store-private-data-in-react-native – Julien Kode Aug 09 '17 at 08:07
  • 4
    Pedro and Julien, stop linking to other posts. That is annoying. If there was useful information then post it here. That way, users who end up here via Google search don't have to keep chaining into additional posts. – Dennis W Mar 28 '18 at 18:05

4 Answers4

119

Just digging into the React Native code, I found the answer.

Android

The React Native AsyncStoragemodule implementation is based on SQLiteOpenHelper. The package where all the data classes are handled: https://github.com/facebook/react-native/tree/master/ReactAndroid/src/main/java/com/facebook/react/modules/storage

The class with the instructions to create the database: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/modules/storage/ReactDatabaseSupplier.java

By the Android documentation, the databases created by the application are saved in private disk space that's associated application, so it is secure.

Just like files that you save on the device's internal storage, Android stores your database in private disk space that's associated application. Your data is secure, because by default this area is not accessible to other applications.

Source

iOS

In iOS the AsyncStorage values are saved in serialized dictionary files. Those files are saved in the application NSDocumentDirectory. In iOS all applications live in their own sandbox, so all files of one application are secured, they cannot be accessed by the other applications.

The code in iOS that handles the AsyncStorage module can be found here: https://github.com/facebook/react-native/blob/master/React/Modules/RCTAsyncLocalStorage.m

And as we can see here the files used to store the values saved by the AsyncStorage are saved under the NSDocumentDirectory (inside the application sandbox environment).

Every App Is an Island An iOS app’s interactions with the file system are limited mostly to the directories inside the app’s sandbox. During installation of a new app, the installer creates a number of containers for the app. Each container has a specific role. The bundle container holds the app’s bundle, whereas the data container holds data for both the application and the user. The data container is further divided into a number of directories that the app can use to sort and organize its data. The app may also request access to additional containers—for example, the iCloud container—at runtime.

Source

Conclusion

It is safe to use AsyncStorage to save user tokens, since they are saved under a secure context.

Please note that this is only true for Android devices without root and for iOS devices without jailbreak. Please also note that if the attacker has physical access to the device and the device is not protected. He can connect the device to the mac laptop and extract the documents directory and see all the contents saved under the documents directory.

njoye
  • 368
  • 4
  • 17
Sandro Machado
  • 8,485
  • 3
  • 31
  • 54
  • 9
    Great answer backed with references and facts. Thank you! – kalana Jan 05 '17 at 07:14
  • 1
    Re: Android: "so it is secure": nothing is secure, a proximity to the ideal of 'secure' can be achieved. In this case the data can be accessed if device is rooted (whether intentionally or not). – straya Apr 28 '17 at 02:49
  • 23
    Like I said in the conclusion. – Sandro Machado Apr 28 '17 at 10:08
  • 1
    "It is **safe** to use AsyncStorage to save user tokens, since they are saved under a secure context." Would it not be **safer** to have the user tokens encrypted as well, as an extra layer? Seems like a lot of users have a rooted Android device and can snoop the files, but not everyone will do the effort to decrypt something. – bart-kn Apr 26 '19 at 09:44
  • @SandroMachado Does AsyncStorage is capable of storing large amount of Masterdata ? – Mayur Baldha Jun 06 '19 at 11:34
  • 1
    @MayurBaldha in Android you can only store 6MB. Check this: https://github.com/facebook/react-native/blob/0.33-stable/ReactAndroid/src/main/java/com/facebook/react/modules/storage/ReactDatabaseSupplier.java#L48 – Sandro Machado Jun 14 '19 at 00:21
  • 3
    The RN documentation specifically states that you should not use AsyncStorage for token storage - https://reactnative.dev/docs/security – nerdlinger May 15 '20 at 20:34
8

AsyncStorage saves key-value pairs as a plaintext JSON file in the Documents directory. It does not encrypt its contents.

This is a security issue (at least on iOS) because it's possible for an attacker with access to the device to obtain a dump of the contents of the sandbox and trivially extract any data saved through AsyncStorage.

This used to not be clearly stated in the docs for AsyncStorage.js, but it is now: https://github.com/facebook/react-native/pull/8809

Also see: https://stackoverflow.com/a/38398114/1072846

Community
  • 1
  • 1
Eric
  • 12,878
  • 13
  • 78
  • 119
4

If someone wants the additional step of having the data encrypted, you might want to look at this: https://github.com/oblador/react-native-keychain

It uses facebook conceal internally.

flaky
  • 4,783
  • 3
  • 22
  • 37
3

I really recommand you to use a library like react-native-keychain to store private data in react-native

For Android API level:

  • 16-22 use Facebook Conceal
  • 23+ use Android Keystore

You can use it like that:

// 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);
  });
Julien Kode
  • 3,072
  • 13
  • 29
  • 1
    Is there a way to store a 2nd set of credentials in the keychain? I have only figured out how to set one pair of values, and I've already used up that slot storing the user's username and password to be used in conjunction with biometric authentication (touchId/faceId). – Dennis W Mar 28 '18 at 18:00
  • 2
    Hey, could this lib be used to store a jwt token? I'm not too interested in storing a username or password, I am looking for a safe way to store a token between my main app (react native) and an iOS share extension that is under the same bundle and app group. If the answer is yes, which API function would be used to set the token in keychain ? Thanks – Rasmus Puls Oct 23 '19 at 13:42