Sunday 4 September 2016

Can Core data be trusted with sensitive informations?

Hi All,

Recently completed 4 years of my professional career as iOS developer. I started working on core data from my 2nd app onwards. For past 4 years, almost every single day I had to deal with core data and honestly speaking I felt like I knew in and out of core data until a question asked by my new boss seems to have shattered my whole confidence.

What the hell did he ask then ?? Yeah, you guessed it right!! The same question that we are trying to find solution for, in this blog.

"Can Core data be trusted with sensitive informations?!! Can you assure me that no body else gets an access to the data in core data except our app? What If somebody gets an access to the app bundle and copies the .sqlite file and opens it ??"

My answer was "Yeah!! I mean, most of the apps in iOS makes use of it, it should be secure ! I didn't test it though, I guess Core data takes care of the data security, it does right ??? In fact I don't know!!! never thought it that way".

A little bit of browsing landed me on the official Apple docs page on "Persistent Store Types and Behaviours" and hardly two minutes of reading changed my perception of core data and its security. Here is what Apple docs says :
Core Data makes no guarantees regarding the security of persistent stores from untrusted sources (as opposed to stores generated internally) and cannot detect whether files have been maliciously modified. 
Now considering that my app deals with the sensitive data of my clients, security of which I could not have compromised with, I really started questioning about the relevance of usage of core data in my app.

But if not core data then what else to use?? My app deals with multiple threads constantly changing the data, Core data provides me a thread safe persistent storage, now the over head of creating a thread safe data storage system with capability to hold the data persistently was immense and un-necessary.

So the only solution left was to find how to make core data secure :)

Let's Secure Core Data :

Solution 1 : Persistent store encryption using Data Protection

Here is the quote from the book Pro CoreData for iOS by Michael Privat and Robert Warner.
One of the security options that iPhone provides is hardware-level disk encryption. Apple calls it data protection. When enabled, it encrypts a portion of your disk when the device is locked and is automatically decrypted when the device is unlocked.
Though that sounds very much sweet, it isn't good enough to leave our data base security completely on it, Isn't it. Why is that???

1. Data protection needs to enabled manually by the user of the app by enabling Passcode security. Thats bad isn't it ?? How can you ask your apps user to enable the Pass Code protection? What will you say  "Sorry, We couldn't code to protect your data so we are leaving it to you and to apple to take care of it!!" ?.

2. As apple is expanding its customer base every day by day, you can't expect all the customer to be of same technical intelligence. Most your app user might not even know that there exists an option called " enable the data protection"!!, In fact I din knew it my self. How can we explain them the importance of it and convince them to enable it and leave the data unprotected on some un-secured device as it is.

3.The data is secure as long as nobody knows the passcode of the user's device. Once a valid passcode has been entered the data gets decrypted. So the app's data is as much secure as the device it self.

Now you might be wondering what a moron the writer is! Once the hacker/attacker enters the passcode and gets an access to the device won't he open up the app and see the data? Now if your app is dumb enough to show all the sensitive data without asking the user to go through any of the app specific security measures then no body can save your app.

On the other hand if you app has its own security measures to provide the access to sensitive data like having its own passcode lock or finger print scan security then your app might not give an access to the secure data.

But because data protection decrypts the file, if the hacker gets the access to the app bundle by using any of the various s/w available he can open up the .sqlite/.xml/or any other custom format file you have and can see all the secure info your app has collected in core data.

4.Finally the last point nails the last nail on the coffin of "data protection" feature. Here is the quote form the same book Pro CoreData for iOS by Michael Privat and Robert Warner.

Another inconvenience with this method is that for very large databases, the time it takes to unlock and start your application might be rather large since a huge file will need to be decrypted before it can be used.
Now you don't want your data protection to slow done the launch of your app or slow down some body else's device don't you ??? We are all aware of consequences we might have to face with that :D

Though I only enumerated the cons of data protection, it does a fair job from its perspective. It does encrypt the data once the device is locked and decrypts it when the device is unlocked for free to all the apps which wants to avail this security.

Though enabling the Data Protection hardly needs few lines of code in your existing app, covering it is not in the scope of this article. I'll discuss about it, sometimes later.

Solution 2 : Encrypt the Data in Core Data.

Though the solution.1 talked about encrypting the whole file it self, it is always possible  only to encrypt/decrypt the data that application puts in into the files rather than encrypting the whole file itself.

How can I do that??

Though core data, in itself does not do much to encrypt the data, it never stops you from doing it either, If you ever try to do so.

Here is how you can do it.

Let's consider a simple Core data entity "Person". This is how a typical Core data entity model looks like.



Now lets add a person to core data using code,


If we open up the .Sqlite file using the apps like SqliteBrowser, this is how you will see the data.








Now that's quite obvious, Isn't it?

Now lets assume the phone number and the nick name are very much sensitive informations and you don't want any one else other than your app to read it. So Let's encrypt them.

Modify your Core data model for Person entity as shown below.



Select the fields nickname and phone and change the data type to Transformable. Now open up the property inspector and click on nickname field, you should see something like,


Now the most important part!!. See that Name field below Attribute type drop down in property inspector, Enter EncryptedStringTransformer and hit enter. Repeat the same steps for phone field as well.

Now what the hell is EncryptedStringTransformer??

Don't worry! Its the name of the NSValueTransformer subclass that you are going to create in a while.

As the purpose of this tutorial is to show how to encrypt the core data and not to explain the encryption itself, I am using a open source Cross platform AES encryptor. Here is a link to its github repository : https://github.com/RNCryptor/RNCryptor

Selection of the encryption algorithm completely depends on your application requirement. There is no restriction imposed by core data as to which encryption algorithm to use to encrypt the data. So go ahead!! Find the algorithm that suites you the most and use it.

Now, for this article, lets go ahead and add the RNCryptor to the project. I am making use of cocoa pods in here, go ahead and use either Carthage or install it manually, whichever way you feel comfortable,


Now lets create the EncryptedStringTransformer about which we discussed just a while ago.


Now go ahead and override transformedValue and reverseTransformedValue methods of NSValueTransformer. These two methods gets called when your application tries to enter the data to /read the data from core data respectively.

Remember you specified EncryptedStringTransformer as the name of the transformer function for the fields nickname and phone, now whenever your application tries to enter the value for these two fields transformedValue function of EncryptedStringTransformer gets called and gives you a chance to transform the raw data to whatever you want. What we do in here is take the plain text, run the RNCryptor and convert it to cipher text and return the NSData.

Similarly, whenever your application tries to read the data from these two fields, reverseTransformedValue function gets called and asks you to reverse the transformation you applied before and return the original data. What we do here is very simple, we decrypt the cipher text, convert it to plain String and return it.

That's it!!! Now your Person entities nickname and phone fields data are encrypted. Lets insert the data to Person entity using the same code I posted above. Everything works just same as before, except for the .sqlite file fields value. Now if we open up the .Sqlite file,





Woo hoo!!! Can you see that nickname and phone fields no longer has text, instead now it says "BLOB".

Solution 2 over comes the issues in Data Protection. Now the data in .sqlite file will remain encrypted no matter whether the device is locked or unlocked. No other application can read the data from .sqlite file as it will not be knowing the encryption algorithm and the key used to encrypt the data. So only your app can read the data.

Though the solution 2 does what it says, it might not always be the perfect solution for securing the core data.

Hang on!! Now why is that??

1. Though it encrypts the data in .sqlite file, leaves the .sqlite file itself unprotected. Attacker still has an access to the schema of the data base or Core data model.

2. Though attacker can't read the data, solution 2 does not prevent him/her from corrupting the .sqlite file itself and hence causing the app to compromise the data security or might affect the functioning of the app itself.

3.Now the quality of encryption directly depends on the algorithm your app chooses to encrypt the data. Weaker the algorithm less secure is the data.

4.Encryption and decryption happening in NSValueTransformer's transformedValue and reverseTransformedValue obviously affects the application performance. More intense the encryption algorithm, much slower output from core data hence much slower the app.

Solution 3 : Using NSInMemoryStoreType persistent store to save sensitive data.

Securing the core data need not only mean encrypting the file. What if we can make use of Core data to its fullest potential and yet not save a word on file in file system?

Yes! Solution 3 talks about using the NSInMemoryStore as persistent store. As the name suggests persistent store used by core data is no longer a .sqlite file, instead persistent store is memory itself.

Now if you are not aware, Sqlite is not the only persistent store type supported by core data. Core data supports 4 Persistent store types.

1. In-memory : Here the entire object graph is kept in memory. Hence this is one of the fastest persistent store available. As nothing is stored on disk, it completely eradicates the question of some other application gaining the access to the data your app collects and saves it in core data.

2. SQLite : This is one of the most commonly used persistent store type. Core data makes use of Sqlite under the hood to to keep the data persistently. .Sqlite file gets created usually under the applications document directory which holds the data for each field in core data entity which we normally open up and browse using Sqlite browsers. Only partial object graph is kept in the memory.

3.Binary (atomic) : You can make use of this store type, if you want store writes to be atomic.

4.XML (atomic) : You can make use of this store type, if you want to create a human readable and externally passable data store. It is very much common to make use of this data store in the initial days of development and then to move on to SQLite data stores once the app makes its way to production. Sad news for iOS developers, XML is store type is not available for iOS till this day. 

Not all the data in core data may be sensitive to the user. For example, lets assume that a part of your app deals with users credit/debit card details or deals with users bank account informations. So for your app sensitive informations are the credit/debit card details or bank account informations. Informations like user's comments, users friends list or user's subscription list may not be that sensitive.

So rather than trying to secure the whole core data lets focus on securing only the part of it which actually requires the security.

To explain it lets consider a scenario. Continuing on the same example that I mentioned above. Lets add another entity to core data and lets call it Account info.




Each person can hold multiple accounts and each account belongs to one person.

Here the information like name, age may not be so important to the user, but information like account number, amount held in that account may be very much sensitive. So lets try protect the AccountInfo entity data from being accessed by the attacker or other apps.

How can I do that??

Simple, Lets open up the core data model. On the left corner, there is a button called Add Entity, Click and hold on it, till a popup appears, now select Add Configuration.



You will see a new Configuration being added. Lets name it as InMemory. Repeat the steps and create another configuration named SQLStorage

Once you are done you must be able to see 3 Configurations. 
1.Default
2.InMemory
3.SQLStorage

Now select the SQLStorage and drag the Person entity from entities list on left side to the entities section of SQLStorage as shown below,

























Now repeat the step for InMemory configuration but this time drag AccountInfo entity to the entities section.

Once done your InMemory Configuration must be having the AccountInfo entity and SQLStorage configuration must be having the Person entity as shown below.

InMemory Configuration after dragging Account info entity

SQLStorage Configuration after dragging Person entity
Now select the person entity from entities list and select the relationship from Person to AccountInfo (holds) and enable the transient property.


repeat the same steps for the inverse relationship (belongsTo) as well.

Believe me you are just few steps away now. Hang on!!

Let's open the AppDelegate's core data stack and lets have look at those mysterious methods that were auto generated for us when we selected Core data while setting up the project.

One of the method that we are really interested here is the PersistentStoreCoordinator getter. Your PersistentStoreCoordinator must be having a statement which looks like


Hmmm there is an option for specifying the configuration and no body told me about it till now !!!!

Lets create two persistent stores using the two configurations that we have created and add it to the persistent store coordinator. One of the persistent store is of type SQLStorage and another is of type InMemory.


Not so sensitive information holding entity Person is stored in SQLStorage persistent store and AccountInfo entity is stored in InMemory persistent store.

Now go ahead and add the data to AccountInfo with respect to some person in core data using the code,

To demonstrate it, I have created a simple list which shows the name of the person and the account number belonging to that person below if the person holds an account else show N/A.




Now lets quit the app and re launch.

Why is it showing N/A for both the entry in the list now ??

Simple, AccountInfo entity was added to the InMemory persistent store, data of the entities belonging to InMemory persistent store exists as long as the app is running and has an allocated memory in RAM. As soon as the app got quit, memory associated with the app was lost hence the persistent store as well resulting in complete data lose of AccountInfo.

Though, the solution 3 isn't suitable for all the scenarios, it might be useful in certain scenarios where the sensitive data is needed and should be associated with some other persistent data as long as the application is running. Every time the app is launched, all these sensitive informations can be synced and a relationship to the existing persistent data can be established providing the user experience as if this data always existed in device.

As the data is never stored in the form of any file on disk, the question of other application gaining the access to sensitive information becomes obsolete and hence ensures the complete protection of sensitive data.

Though Solution 3 ensures the complete protection of sensitive data, It can not be used as the perfect solution in all the scenarios, because of the following reasons.

1.As the data is never saved on disk, the concept of persistent storage completely fails here. In fact saving the data in InMemory persistent store is equivalent to holding the data in application scope variables (Singleton variables).

2. If your app deals with offline capability and needs the access to the sensitive information in offline mode this method fails miserably.

3. Though using the InMemory persistent store provides a thread safe memory based persistent store which allows us to enjoy all the benefits of core data like relationships, fetch requests, FetchedResultsControllers, In effect they are like any other primitive data types in memory as they loose their data once the app is killed hence completely fails the aim to achieve offline storage.

Solution 4: Using SqlCipher with Core Data.

SqlCipher is an open source extension to Sqlite which can be used to encrypt the entire .sqlite file on disk.

Pros : .Sqlite file gets completely encrypted and no other apps can read the data from it other than the one which created it.

Cons:
1. Does not work directly with core data. You will have to make use of a project on git hub to make it work with core data.

2. Works only with .SQLite persistent storage type.

Integration and exploration of SqlCipher with core data is not in the scope of this post.

Conclusion:

1. None of the four solutions proposed above are complete in themselves, but they might prove to be effective under very specific scenarios.

2.Selection of which solution to use or the combination of solutions to use completely depends on the apps requirements. Its up to you to decide which solution to use depending on your apps demand.

There are always more than one way to solve a problem, which one we pick depends on the situation. If you happen to encounter any other solution, that I failed to enumerate here, please leave a comment below.


10 comments:

  1. Really a good post about Coredata security techniques .

    It would be have been more nicer, If you have explained about various FileProtetcion types like complete, unlessopen etc..,

    Good Post !

    ReplyDelete
    Replies
    1. Thanks a lot for taking time to read the post and appreciating it :) Sure I would consider FileProtection types and would try updating post once I get time :) Thanks for the suggestion

      Delete
  2. Very nice post, explains all the aspect of core data security. If you could add some more info about 'Encrypted Core Data' project in Solution4, it would have been so nice and appreciated.

    ReplyDelete
  3. ally this post is so much helpful for me. Your blog shares some unique information. Thanks for sharing this informative article. Anybody can encrypt files online free here.

    ReplyDelete
  4. Is it safe to store password as string in the code?

    ReplyDelete
  5. I think this blog is welly explained as far as possible about core data security

    ReplyDelete
  6. If you are looking for more information about flat rate locksmith Las Vegas check that right away. www.meridiannorstar.net

    ReplyDelete
  7. Nice article on core data security!

    ReplyDelete