Open abejfehr opened 2 years ago
I just managed to reproduce this, here would be the reproduction instructions:
The hand wavey working theory is that the key used to encrypt the contents of the store is somehow device-specific, so when those files are restored they can't be read/written by the app on the new device
@abejfehr Ofcourse the key is device specific since it is auto-generated internally and stored in the Keychain.
So on a new device the only way to decrypt the same storage would be to somehow be able to have access to the same Keychain on the primary device. I am not sure if it is possible to share the Keychain or sync it?
Other than that, the only other way is to use a custom key which you will manage and not the library. If the library has to manage the key then you will have to somehow move the key to the other device when a person logs in etc.
In one of the reproductions the keychain was synced via iCloud and this issue was still present unfortunately 🙁 so based on that it seems like using this library for encrypted storage without a custom key is just expected not to be stable between devices?
@abejfehr It seems that setting the kSecAttrSynchronizable
is required to make the values syncable. I don't think we are setting such an attribute currently. Setting that should solve the issue.
https://stackoverflow.com/questions/42903633/store-item-in-ios-keychain-without-icloud-sync
But that would only resolve the issue for folks who choose to sync their keychain, right?
On my iCloud for example, I have "iCloud Backup" set to "on" and "Keychain" set to "off", so my keychain and my app backups would be out of sync and my app wouldn't survive a backup and restore.
Even besides getting backup & restore working, I'd like to have the following to at least be able to mitigate it:
On that second point, the most we can do right now is clearStore, and it doesn't do enough to resolve the issue
What about verifying on app load that the key works with the store, and entirely deleting the store if it does not? That way there's not an unusable MMKV store
I agree. There should be a way to work around this. I will look into it. The simplest way would be to exclude mmkv files from cloud backup and throw some error upon which store can be deleted and recreated possibly.
Des anyone know how to solve this issue ?
@ammarahm-ed , @abejfehr , any solution yet?
Still no idea @ammarahm-ed ? Checking myself, but currently also no solution.
@jarnove I have no solution yet 😢
I actually attempt to write/read to the encrypted store 3 times and if it fails I ask clients to uninstall and reinstall the app to get around this. It's not a good UX
It's my understanding that setting kSecAttrSynchronizable = true
would solve this issue only when a) user hasn't opted out of iCloud keychain backup and b) the restore is to the same device.
keychain items are included in the backup but they are wrapped with a device-specific key. Thus, they can only be restored to the device that originally backed them up, which means that they get lost when you restore the backup to a different device
https://developer.apple.com/forums/thread/93373?answerId=282724022#282724022
@abejfehr @jarnove Did either of you try making code changes to remove MMKV files from backup with isExcludedFromBackup
?
@Mookiies , not yet, do you have an idea on how to implement it? And I'm afraid that this will not work for existing users, right?
I took a stab at implementing it by excluding the entire mmkv directory,/Library/mmkv
, in this commit: https://github.com/Mookiies/react-native-mmkv-storage/commit/165cd18ad235dd27c9356a2dc8a27dcba5559ba0
It's a naive implementation, but I think it would be enough to verify that it would work. I've yet to verify as I don't have a working iCloud backup yet.
If it does the trick I imagine at the minimum in order to get a mergable PR ready it would need to be made configurable, as excluding shouldn't be the default especially for unencrypted.
To follow Apple's recommendations 100% these improvements could also be made
isExcludedFromBackup
more frequently? (Apple docs recommend setting it each time a file is changed)/mmkv
directory with the app's bundle identifierIt's also worth noting that isExcludedFromBackup
does not provide a guarantee that a given file or directory won't get backed up.
The isExcludedFromBackup resource value exists only to provide guidance to the system about which files and directories it can exclude; it’s not a mechanism to guarantee those items never appear in a backup or on a restored device.^1
Given that, it seems an ideal solution would also include a way to somehow recover if some of the encrypted files still managed to get backed up.
Hey. I have a few users with a similar issue, but I'm not sure it's the same. Is a new device/restore really needed or do you have a simpler way to test this? https://github.com/ammarahm-ed/react-native-mmkv-storage/issues/269#issuecomment-1185939759
Hey. I have a few users with a similar issue, but I'm not sure it's the same. Is a new device/restore really needed or do you have a simpler way to test this? #269 (comment)
No, unfortunately I was only able to reproduce this issue with 2 iPhones
The error today happens because when creating the storage, the library uses this property: kSecAttrAccessibleAfterFirstUnlock
And reading apple docs of this property you have this information: After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
What happens is:
There are two solutions:
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
setAccessibleIOS(IOSAccessibleStates.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY)
or
setAccessibleIOS('AccessibleAfterFirstUnlockThisDeviceOnly')
encryptWithCustomKey('your-custom-key')
Unfortunately the documentation is not clear enough, and this error is "ignored" by the library, so if you use encryption you must follow one of these two steps.
@GleidsonDaniel Have you been able to verify that solution 1, setting AccessibleAfterFirstUnlockThisDeviceOnly
, solves this problem for a different device iCloud restore?
It's my understanding that this wouldn't solve the issue because regardless of your keychain settings, the encrypted MMKV data in /Library/mmkv
is going to get backed up. Therefore, causing the issue as the library cannot decrypt the backup-restored MMKV data as it no longer has the keys.
@GleidsonDaniel Have you been able to verify that solution 1, setting
AccessibleAfterFirstUnlockThisDeviceOnly
, solves this problem for a different device iCloud restore?It's my understanding that this wouldn't solve the issue because regardless of your keychain settings, the encrypted MMKV data in
/Library/mmkv
is going to get backed up. Therefore, causing the issue as the library cannot decrypt the backup-restored MMKV data as it no longer has the keys.
Why would you do the backup if you are saying not to do the backup? The backup only happens because, as I said, the keys are saved using the property that says to save the backup.
Here's where the library returns the property: https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/2aeabecd6a9c9092ea81e19a99cf853539dfe6f2/ios/SecureStorage.m#L253
This is where the library gets the value telling how the property should be saved in the keychain: https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/2aeabecd6a9c9092ea81e19a99cf853539dfe6f2/ios/SecureStorage.m#L159
Nothing happens "automatically", not all data is uploaded to icloud, this only happens because the lib explicitly returns that this will happen, the data must be saved in the icloud backup, if you change this behavior the backup will not happen, simple as that. Whether the dev of the lib made this aware or not is another question. However, the backup of the keychain keys only occurs because of this. Soon just change the way the keys are going to be saved and the problem is solved.
The contents of the Library directory (with the exception of the Caches subdirectory) are backed up by iTunes and iCloud
When iCloud Backup is in an enabled state, it periodically creates a backup of the user’s device, including your app’s data.
And this library stores encrypted data in /Library/MMKV
.
This issue might not be limited to just iOS/iCloud. We received the same reports from some Android users having a new phone and restored their backups.
My understanding is the key is new but the store coming from the backup is encrypted with the old key. For our use case it would be fine to simply remove and recreate the store if that happens. Is that possible?
Has anyone got an update or figured this out already? As @mjooms mentioned removing the encrypted store would work fine for my casen as well but that doesn't seem like an ideal fix. (And I don't think we can do that anyway 😞). Seems to me that the only viable solution would be to generate our own encryptionKeys like @GleidsonDaniel mentioned before.
@Mookiies would you mind if I implement the 2 remaining bullet points for your commit? As this might be the only right way to fix this issue. The only thing we would need is a proper way to validate if this fix works.
@Mookiies would you mind if I implement the 2 remaining bullet points for your commit? As this might be the only right way to fix this issue. The only thing we would need is a proper way to validate if this fix works.
The best thing is to change the library since its owner has no interest in maintaining it any longer, just seeing that a critical bug like this is completely ignored. It's in our plans to change the lib and everyone reading this thread should do the same.
We were between using our own keys and completely disabling the backup, but we decided to disable it, given that we will soon be changing libraries. https://github.com/RocketChat/Rocket.Chat.ReactNative/pull/5165
I'm thinking about moving along too.
@GleidsonDaniel which alternative are you considering to use instead?
Thanks for letting me know, I will propose it to my team as well and we'll see what we will do.
Describe the bug Either getting or setting in storage with encryption is failing.
For context, this is the wrapper around the library:
and this being the set of actions taken:
To Reproduce I have no consistent reproduction steps 😅 it's happening to a lot of people, but only rarely. On devices where this happens, it happens consistently. It's almost like
react-native-mmkv-storage
is "broken" for that device.Expected behavior Setting a value synchronously should allow you to get it right after.
Platform Information:
Additional context On the current device we have that has this issue, a recent restore from iCloud was performed, but it hasn't been proven that this is the cause.
It's worth noting that we're on version 0.7.6 of this library, which gives us the following dependencies in Podfile.lock:
What's interesting about that is that backup & restore was introduced in MMKV 1.2.11, so our version of MMKVCore would have that functionality, but MMKV does not. Is that an issue?