Securing paywalled features macOS app

Hello,

I'm currently developing a SwiftUI app for macOS that includes both free and premium features. One of the features is the maximum amount of items that can be stored in the app: the free version allows users to store up to 10 items, whereas premium users can store up to 1000.

The issue I'm facing is with securing the premium feature. I'm using UserDefaults to store the value of the feature, with a key named maxStorageSize. The user can configure this using a dropdown (there are also other options between 10 and 1000). For non-premium users, this dropdown is disabled and set to a default of 10. For premium users, the dropdown is enabled and thus the user can change the setting to a higher value.

However, I've realised that this limitation can be bypassed by using the defaults CLI tool to change the value of maxStorageSize underneath the application's knowledge, effectively circumventing the premium requirement.

My current workaround is:

  • check the user defaults on launch and if the user is non-premium and any of the premium features have been changed: reset them.
  • continuously monitor for updates on the defaults and if the user is non-premium and a premium feature value has been changed: reset it.

This seems to work but feels a little hacky. I'm wondering what mechanisms you've come up with to solve this.

What about using an encrypted token that gives access?

Here is what I would try. That would not be bullet proof, but an initial level of protection.

Write your own encrypt/decrypt func to convert the value of maxStorageSize into an Int that will be the encrypted value

  • store this encrypted value.
  • on decrypt, you should check you get a valid number (10 or 1000)

Additional suggestion (to be thoroughly analysed however)

  • to further protect and avoid someone copying the value from another device, you could combine with an device dependant information such as identifierForVendor (https://stackoverflow.com/questions/21602161/how-to-get-device-serial-number-programmatically-in-ios)
  • you could compute an Int from this String and multiplex it with maxStorageSize : maxStorageSize + 10 000 * computed Int
  • then encrypt this value.
  • on decrypting, after demultiplexing if you don't get the correct value for identifierForVendor, you should not validate the 1000 option.
Securing paywalled features macOS app
 
 
Q