I need to generate a device ID that complies the following criteria:
It is universally unique.
It is consistent (as much as possible), i.e. it stays the same for the same device.
It requires minimal permissions.
It is legal to be transferred and stored.
The solution, for example, could be a hardware ID which is accessible with no special permissions which is then hashed and sent over the wire, I just don't know if there is any.
P.S. I don't want to store anything in the keychain or anywhere else. Just generate on the fly, and remain the same, independently from the app it's being generated on. And resetting it during factory reset or other rare occasions is fine.
While I could access some device specific token in the past, this is not possible any longer.
The API will serve you with a unique identifier for the app installation on that particular device. It seems storing that token and sharing it using the keychain (if not already present from another app) seems to be your best bet at the moment.
This is a necessity to protect users' privacy, and I strongly suggest not to work against that and compromise the concept (and I think it's prohibited in any AppStore app anyway).
Related
I am migrating our organisation's app to a new developer account, for internal reasons. I've learned that upon transferring the app to the new account, and after we release an update for the app from the new developer account, we will incur a one time loss of keychain data.
My concern is, that we cannot afford this loss of data. Due to security reasons and nature of the type of clients using the app.
So my question is 2 part;
1) Is there any way that I can prevent this keychain data loss?
2) If not, what are the alternate strategies I can use to prevent the users from getting logged out during this transition?
Assuming you're talking about a real, full App Transfer I don't think this is possible. Keychains are not just differentiated via the app ID, but also the organization's ID (or more precisely the organization's ID is part of an app ID). Since the app's ID de facto changes with a transfer, iOS will prevent it to look up its old keychain. Not even app groups get around this, as a group cannot consist of apps by different organizations AFAIK. Apple would need to implement a kind of exception to this for at least a certain "migration period", but I doubt that will ever happen (for security considerations).
The only way to circumvent a re-login so that the transferred app can save the credentials in its "new" keychain would be to first have an update for the old, not-yet transferred app and have it save the credentials elsewhere, then have the transferred app look them up from there. I'd strongly recommend against this for security reasons, but if you absolutely must do that, you could upload the credentials somewhere secure (via an encrypted connection, obviously, i.e. SSL). You might even be able to use shared web credentials for this, but I haven't looked into that too deeply, so I am not sure whether both apps can be configured to be associated with that website (they're coming from two different teams, after all, but it might be allowed).
Even then, some users might "miss" the update of the older app before the transfer (and update to the "new" one), then they would have to re-login anyways.
If you follow this idea, be aware that you're "moving around" users' private data in a new way! Considering the recent GDPR fuzz this might even have string legal implications, so check with your company's legal advisors about this, too!
1) There is no way your can prevent data loss, since the App ID Prefix has changed during app transfer TN2311 > A one-time loss in keychain data will occur if you switch your App ID prefix
2)
If you application utilizes Push Notification, you can use the APNs device tokens to remap the user account.
Once the new app is launched:
Send the device token to server
Find the username from your database
Generate a session token (add your own security design here)
Save username + the token in your new Keychain
For those who doesn't allow notification, just force them to relogin
A while ago, I got banned from an iOS app. Not a user ID ban, but it seems to have been a device ban. Even if I delete and reinstall the app and try to make a new account, it would automatically get banned. My question is, how are they doing this? What are all the possible ways to ban a device that is persistent even after deleting and re-installing the app?
It is not possible to access UDID anymore, so there's that. And I don't think iOS allows them to view iTunes account, so that's not possible. Are they perhaps storing anything in the keychain? From what I know, some things in the keychain don't get removed even if the app is uninstalled.
I'd like to implement something like this in my app, so I want to know all the possible methods, with pros and cons for each.
When the app is uninstalled, all data goes with it. So writing anything to disk isn't going to work.
You can store something in the keychain, although users can edit the keychain if they sync it to a mac. That makes it pretty insecure.
The best option is to store it off the device, in iCloud. I'd go with key/value storage: https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesigningForKey-ValueDataIniCloud.html
Another option you could choose is Apple's replacement for reading the UDID which is to grab the advertising identifier. However it is possible for users to block this in Settings and your app could theoretically be kicked out of the store if it's used for something other than the intended purpose. I don't think it's actively policed, but still probably not a good idea to use it.
A belts and suspenders option would be to do everything:
save it to disk (in application support)
save it to key value storage in icloud
save it to keychain
use the advertising identifier to and send it to a remote server that you control (personally I'd skip this to avoid having the app banned from the store)
However... I'm not sure if Apple allows users to be banned. You might well be violating the developer rules by doing so. Especially the last one, which goes something like "this list is incomplete and constantly changing, your app might cause us to add a new rule to the list".
If it's a paid app or has in app purchases I'm pretty sure they would issue refunds to any customer who complains, and they'd probably follow the refund up with kicking your app out of the store.
Were I tasked to implement something like this, I would do exactly what you suspect: generate a simple random token, keep it in the keychain, and include it in API requests.
Nothing in the iOS keychain is removed when the app is uninstalled. I don't actually know of a good way to manually remove stuff from it.
according to Apple's document, the app's vendor ID is calculated based on bundle ID, and from my understanding, it doesn't related to a device's UUID in any way, which means nobody can tell the device from the vendor ID alone.
I am planning to upload the vendor IDs from my app to my server to help me keep track of my users.
So, I would like to know: is it OK to upload and store them in plaintext?
The identifierForVendor in itself is not sensitive data but if you are effectively using it as a password (actually a username and password all in one), then it becomes sensitive data because of what it represents and what somebody can do with it if they happened to compromise your database. In your case, it seems it would be the equivalent of storing passwords in plaintext, which is generally not a good idea.
Another thing to consider while not exactly related to your question is actually how and when this ID is generated. It will not work as a permanent identifier since it only remains the same for a given device for a given vendor as long as at least one app from that vendor remains on that device. Basically, if a user installs your app, then reinstalls it (and they don't have any other apps from you on their device at that time), the ID you get will actually be different, and they will be considered a different "user". Because of this, it is not a great idea to try to use this identifier as a permanent login mechanism.
To contextualize the question, our use case is an app through which users can get free promotional items on signup. We would like to prevent abuse of the system by limiting to one promotion per device. Since we can't access the UDID, we need some other (mostly) reliable way to check if the phone has already signed up an account. We don't need a solution that is impossible to circumvent, just one that is highly inconvenient to circumvent.
If we store a unique key in the user's keychain, then we'll be able to read it again even if they uninstall and reinstall the app. We're considering using this method to track devices that have already signed up accounts.
Questions:
Is there a relatively easy way that the user could delete or change our app's keychain data? You can assume that the user's device is not jailbroken, and that they will not go to the trouble of completely reinstalling the OS.
What are other options besides using the keychain we might consider? Keep in mind that the app communicates with our server during signup, so we can store previous signup information of any kind on the server.
Are there any gotchas or problems with the keychain method we should know about?
I have a woocommerce store with a plugin which generates license keys.
The idea is that the user of my app, will enter a license key which will be checked against a server. If it is correct then the app will create a special file/persistent flag some how.
Once activated, the user can reset the device or whatever and the app will simply check to see if this persistent file/flag is there.
My questions are:
Is there a library available specifically for this sort of thing?
If not, what is the best/standard way of doing this so the file can't be copied to another device to fake an activation?
Could I use something untouchable inside the application bundle that isn't a file to do this?
Thanks guys, some guidance or suggestions would be great.
The "standard" way to do it is to have Apple manage the licensing for you via in-app purchasing or subscriptions, they provide a secure implementation for checking if something is paid for, but this may not be an option for you.
If you need to roll it yourself, you need to use assymetric encryption, with a private key on a server that nobody can access (except you) and a public key distributed in the app. You also need the [UIDevice currentDevice].identifierForVendor.UUIDString value on the device, which is a value unique to that device which cannot be changed by the user (unless they jail break).
The server encrypts some data like 2014-03-23,purchased-three-month-license,<device-identifier-for-vendor>,<long-random-number> and gives the encrypted data to the device as the license key.
The device has the public key, and uses it to decrypt the license key. Then it checks the result to see if the date is in the last three months, the device identifier matches the device's actual identifier, and verifies that the long random number is there and at least 10 digits or something (it doesn't matter what the random number is, it just needs to be there).
Anybody who jail breaks their device will still be able to bypass your security. There is no way to prevent this. But it will take the user a lot of work, they'll probably just pay instead.
All the libraries you need for the encryption are built in, but they're low level APIs and a bit complicated to use and it's easy to screw it up. I recommend RNCryptor as a high level wrapper around them.
Also note that if the user uninstalls your app, then re-installs it (or sometimes when Xcode installs a new build) the identifierForVendor will change to a new value. Apple enforces this to protect user privacy, there's no way around it. You will need to have the server re-generate a new license key based on the new identifier... perhaps by asking the user to enter an email address and password. You can prevent piracy by monitoring how many times a particular email address is used. If they generate license keys 5 times in 30 days, then you could flag them as a pirate.
Whatever license key the "woocommerce store plugin" is generating will not work, because it won't be using [UIDevice currentDevice].identifierForVendor.UUIDString as part of the license key generation. Without that value, it's going to be trivial for anybody to pirate your software.
There is no such thing as "something untouchable inside the application bundle". The only such thing is the Secure Enclave in Apple's hardware. And to protect user privacy app developers are not allowed to use it directly. identifierForVendor is the only option.
In older versions of iOS there were more options, but they were abused by advertising companies tracking Apple's customers, and so one by one Apple has blocked access over the years.