oblador / react-native-keychain

:key: Keychain Access for React Native
MIT License
3.15k stars 520 forks source link

Keychain is cleared randomly on iOS #409

Open mhv1 opened 3 years ago

mhv1 commented 3 years ago

We've been using this library for a long time and all has been fine, except for few users in production, which seem to suddenly lose all passwords for our app stored in the keychain. We don't yet know if this is an issue with this library or something that just happens for any other reason on iOS.

This has caused issues with our Realm database and state encryption, since we store the encryption key in the keychain. These issues have never manifested on development/QA builds and only happen to a relatively small, but significant, amount of users in production.

So far we have ruled out the following scenarios which may be causing items in the keychain to be nil:

No error has ever been thrown while accessing the keychain. The 'getGenericPassword' method simply returns nil. Only one call to the keychain is done at a time. When the app is started we access the keychain and all the entries are gone, however subsequent writes and queries work just fine. This is the main problem for us as the encryption keys are being re-generated and the database encrypted with the old key is not accessible anymore.

Here is a mock snippet showing how we access the keychain:

const encryptionConfig = Platform.select({
  ios: {
    service: 'com.foo.ios.encryption’,
    key: 'ios_encrypt'
  },
  android: {
    service: 'com.foo.android.encryption’,
    key: 'android_encrypt'
  }
});

const keychainEntry = await Keychain.getGenericPassword({service: encryptionConfig.service});

Unfortunately I cannot provide any sample code that reproduces this issue, as all the errors we've seen happen in a production environment. Attempts to kill the app on startup, update the app either from TestFlight or locally, update the OS, try on different devices on different versions of our app (development and production), have not produce the issue either. All iOS certificates, entitlements and property lists have been checked and nothing has changed for any particular release.

The reason why I opened this issue is because the error has been happening more often now, specially after we updated React Native to version 0.62.0 from 0.61.5, although we don't know if this has anything to do. We would also like to know if anyone else has experienced similar issues or know of any possible causes. Thanks!

RikSchefferOberon commented 3 years ago

it could be that the data becomes inaccessible if the user removes or adjusts their device setup for Touch ID / Face ID

mhv1 commented 3 years ago

Thanks for the tip @RikSchefferOberon ! That sounds like a reasonable cause, however I've not been able to reproduce it myself. We did manage to get hold of one of our affected customers but they didn't mention anything about changing their passcode or any other security method. Will continue looking into this and other probable causes. I probably should start implementing some sort of safety mechanism for when this error happens again.

mhv1 commented 3 years ago

I tried to reproduce based on the suggested cause and using a fresh device:

I still haven't been able to reproduce the issue. If someone has any other suggestions/ideas, or had been able to reproduce this issue in the past, please let me know :)

thijs-qv commented 3 years ago

I have not yet been able to reproduce the issue, but it seems some users are also experiencing this. The only consistent thing I've found out: when this happens autoFill also stops working (in webView) but only for "this" app. It still works fine for other apps.

Edit: now reproducible on some devices (SE2) by force closing the app and then opening it again. It may have to do with the accessible attribute being set via options when inserting a keychain item, and the accessible attribute not being used when querying for an item, but I'm still investigating.

I'm saving the items with accessibility unlockedThisDeviceOnly. Setting this on the query when retrieving an item causes an invalid parameter exception, so this doesn't solve it.

It has to do with the keychain being accessed too early after the app has been launched. Best thing seems to be to use isProtectedDataAvailable and protectedDataAvailableNotification, but for now as a quick workaround I'm rechecking the keychain after 1500ms when it returns nil.