A Guide to Securing your Core Data on an iOS Device

There are numerous ways to secure data stored on an iOS device.

Core Data is a popular choice for implementing data persistence in iOS and OS X applications. Usually, CoreData utilizes an embedded SQLite database. Since iOS 8.3, it’s impossible to access arbitrary data in an application’s sandbox. Additionally, developers may choose to enable “Data Protection” option for their app, which will encrypt the sandbox if the device is locked with a passcode. But we cannot always rely on the end customer to passcode protect his/her device. The device can be jailbroken, or the OS can contain unpublished security flaws. There are external tools like iFunbox and others that help in jailbreaking the device and providing access to the iOS root file system. As the data is shifted to mobile devices, securing data is of supreme importance.

Guide to secure your databases in an IOS Device 1

Mission Secure

With data being shifted to devices, we need a way to ensure encryption of data stored in db files. One of the best ways of encrypting CoreData is by using SQLCipher through Encrypted-Core-Data. I found Encrypted-CoreData-Store best and easiest to implement in an iOS Project. It is an open source library which uses SQLCipher to secure the CoreData. SQLCipher is a specialized build of the excellent SQLite db that performs transparent and on-the-fly encryption. SQLCipher is added as a git submodule inside Encrypted-Core-Data.

Implementation

There are different ways to integrate Encrypted-Data-Store. The standard way is to add the Encrypted-Data-Store and SQLCipher XCode project to your project and build the SQLCipher libraries. You can also install it through CocoaPods. Read more about the standard project setup here.

I had a few restrictions while integrating Encrypted-Core-Data. First, the other teammates need not setup anything at their end after pulling the Encrypted Core Data commit. Had I followed the standard way, other teammates either had to install/update the pods or setup the SQLCipher submodule. As a solution, I decided to build SQLCipher libraries manually and include all required files as part of static library used to integrate Encrypted-Data-Store.

The process to build the SQLCipher manually:

  1. To begin with, clone the Encrypted-Core-Data (Not in your project hierarchy).
  2. Add the Incremental Store folder to your project folder. This folder contains the EncryptedStore.m and EncryptedStore.h files.
  3. Now you need to get the SQLCipher submodule. Open the terminal and go to the encrypted-core-data folder. Put in these commands to get the submodules.
    1. git submodule init
    2. git submodule update
  4. Now comes the cool part of compiling SQLCipher. Run the following commands in the given sequence in the terminal:
    1. ./configure –enable-tempstore=yes –with-crypto-lib=commoncrypto CFLAGS=”-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2″
    2. make sqlite3.c

Built sqlcipher lib

Now we have all the files required to build the SQLCipher library. Double click and open the sqlcipher.xcodeproj in Xcode and build the project. After building the project successfully, go to the Products folder, click on libsqlcipher.a and click Show in finder.

libsqlcipher.a is the static SQLCipher library. Copy libsqlcipher.a to your project structure. Go to Encrypted-CoreData-Store folder. Search sqlite3.c and place it in your project folder.

After adding all required files, your folder structure should look something like this:

Folder Structure

Make sure you have libsqlcipher.a in the Link Binary with Libraries. Now add the following flags in other C Flags under Build Settings:

    1. -DSQLITE_HAS_CODEC
    2. -DSQLCIPHER_CRYPTO_CC

Flags

We now have the SQLCipher library and Encrypted Core Data files in our project.

Encryption

Let’s use the Encrypted-Core-Data to perform actual encryptions.

In our AppDelegate (if we are using the default template generated by Xcode) we have the following code which gives us the PersistentContainer:

- (NSPersistentContainer *)persistentContainer {
   @synchronized (self) {
       if (_persistentContainer == nil) {
           _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
           [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
               if (error != nil) {
                   NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                   abort();
               }
           }];
       }
   }
   return _persistentContainer;
}

Now we need to create the persistent container with EncryptedStore. Use the NSPersistentStoreDescription as below:

Encrypted

Import the "EncryptedStore.h"
- (NSPersistentContainer *)persistentContainer {
   if (_persistentContainer == nil) {
       _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
       NSDictionary *options = @{
                                 EncryptedStorePassphraseKey : @"YOUR_PASSPHRASE",
                                 EncryptedStoreFileManagerOption : [EncryptedStoreFileManager defaultManager]
                                 };
       NSPersistentStoreDescription *description = [EncryptedStore makeDescriptionWithOptions:options configuration:nil error:nil];
       
       _persistentContainer.persistentStoreDescriptions = @[description];
       
       [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *description, NSError * error) {
           if (error) {
               NSLog(@"error! %@", error);
           }
       }];
   }
   return _persistentContainer;
}

You can now use this persistent container for data access.

If you try to open your SQLite file in SQLIiteBrowser, you will face an “Invalid file format” error. I also checked the hex dump of the SQLite file to verify the encryption.

Here are the representations of Non-encrypted Hexdump and Encrypted Hexdump:

Non-encrypted Hexdump

Hexdump non encrypted

Encrypted Hexdump

Hexdump Encrypted

Disadvantages

  1. Key Management
    1. To encrypt and decrypt you need a key. SQLCipher does some of the hard work for you. It encrypts a password into a key using PBKDF2 (Password-Based Key Derivation Function 2). The password still needs to be managed by the developers, assessing how to store this password.
  2. Legal Issues
    1. Apps that use robust cryptography, as the algorithms used in SQLCipher, have obligations for compliance to go with the strict United States export requirements. You should consider these export requirements and your obligations, before adding any encryption.

 

Thus, concludes our blog on ‘A Guide to Securing your Core Data on an iOS device’. Please send your questions and suggestions to akash.raje@cuelogic.com.