Blog

Securing your app with encrypted-core-data

05 Oct, 2016
Xebia Background Header Wave

It’s true that Apple cares very much about user’s privacy. 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. Nonetheless, some extra protection wouldn’t hurt, if it didn’t involve too much work.

Core Data is a popular choice for implementing data persistence in iOS and OS X applications. Typically, it utilises embedded SQLite database. One of the things we could try is to encrypt it. It may seem like an overkill on iOS, since application’s sandbox is not accessible by third parties. But still, the device can be jailbroken, the OS can have unpublished security flaws, or maybe we might want to backup the database somewhere in the cloud. In such cases, the extra protection might become useful. And of course, there is OS X, where application’s data is exposed.

If you want to improve your app’s security, and you also use Core Data, you may try encrypted-core-data, open source library that replaces default database storage. Encrypted Core Data internally uses SQLCipher, which provides transparent encryption of the whole database.

How to integrate

Setting up an encrypted database is quite simple. It involves adding the library to the project, and changing few values during Core Data setup.

  1. Add „EncryptedCoreData” to your project via CocoaPods:
    pod 'EncryptedCoreData'
  2. Find addPersistentStoreWithType call in your code. Add passphrase to options (ideally stored in the keychain, or obtained from backend via secure connection), and change store type to EncryptedStoreType:
    let options = [EncryptedStorePassphraseKey : "key"]
    try coordinator.addPersistentStoreWithType(EncryptedStoreType, 
    configuration: nil, URL: url, options: options)

At this point, the app is configured to use encrypted sqlite, with the important caveat that migration from standard to encrypted (and vice-versa) is not supported. Encrypted-core-data uses different naming scheme for rows and tables, so it’s not as simple as encrypting an existing database. In this case, the only possible option is either to perform migration manually, or not at all (if app has not been released yet).

Performance

Understandably, encrypting data on the fly will consume more CPU time, making Core Data related operations slower. This also means larger energy consumption and so on. To test that, I created few benchmarks that would perform exactly the same operations on standard and encrypted storage, and compare the results.

Note about the tests

  • All measurements are in milliseconds.
  • Tests were performed twice, with a smaller database (charts on the left, approximately 8000 objects), and a larger one (charts on the right, approx. 32000 objects).
  • On charts, the shorter bar is better (operation executed faster).
  • The scheme used for this tests is a simple model of a blog, where users can publish posts and comments.
    1-1
  • Migration involved deleting one class, adding another, and adding and removing properties.
  • Tests from charts were performed on iPhone 6 and iPhone 5s, both with iOS 9.
  • Most recent encrypted-core-data from a master branch was used.

Creating database objects

In this test, a large number of objects has been inserted into the database, then modified, and then deleted (saving between each action).

Small count – iPhone 6Large count – iPhone 6
2-23-2
Small count – iPhone 5sLarge count – iPhone 5s
4-35-1

Encrypted Core Data performed significantly worse, especially with smaller datasets. It might not be a problem if your app doesn’t modify database too much, or only few objects change simultaneously. Otherwise, the waiting time may be quite noticeable for the user, especially if modifications are preceded by requests to the backend.

Fetching objects

Another test involved performing queries on the database with varying levels or complexity, and count of fetched objects.

Name

Predicate

Count(small)

Count(large)

long query,
small result
content like 'test' 
AND author.name contains 'john2' 
AND date >= %@ 
AND author.email contains[cd] '@'
118778
long query,
big result
content like 'test' 
AND author.name contains 'john' 
AND date >= %@ 
AND author.email contains[cd] '@'
201816005
count query
comments.@count >= %@
2020
combined AND comparision
date >= %@
AND author.name contains 'john'
201816006
date comparision
date >= %@
201816006
text comparision
author.name like 'john4'
2001600
fetch all
TRUEPREDICATE
40003200

Here are the results:

Small count – iPhone 6Large count – iPhone 6
6-27
Small count – iPhone 5sLarge count – iPhone 5s
89

Overall, encrypted-core-data seemed to perform pretty well, in some cases better than Apple’s storage. One surprise was the result for fetch request containing count (i.e. fetch all users who wrote more than 5 comments). It seems that SQLCipher is poorly optimised for this task, perhaps decrypting more data than necessary. On the other hand, a long query with a small result and text comparison took longer on the original SQLite store.

There is another problem, not seen on the chart: NSSQLiteStore fetches all scalar properties of the fetched objects, while encrypted-core-data only their identifiers. This leads to huge performance penalty when reading fetched objects. Here is an example:

let result = try! stack.context.executeFetchRequest(request) as! [Comment]
for comment in result {
    _ = comment.content
}

Fetching objects and reading property

 

Small count – iPhone 6Large count – iPhone 6
1011
Small count – iPhone 5sLarge count – iPhone 5s
1213

For encrypted storage, there is an extra SQL query for every accessed object. Therefore, the more objects are fetched and read, the more time it takes. This is especially apparent for a query fetching all objects from the database. At least data is not fetched again if we want to check another property on the same object.

Aggregate functions

Small count – iPhone 6Large count – iPhone 6
1415
Small count – iPhone 5sLarge count – iPhone 5s
1617

A single value (like average, count, etc.) was calculated from all existing objects. Results are comparable. Surprisingly, original SQLite was slightly faster than EncryptedStore on iPhone 5s, but slightly slower on iPhone 6.

Migration

Small count – iPhone 6Large count – iPhone 6
1819
Small count – iPhone 5sLarge count – iPhone 5s
2021

Finally, I measured the time it takes to perform lightweight, automatic migration. Encrypted Core Data performed pretty well for a small data set, but so-so for a bigger one.

Unfortunately, a more complicated migration (like using a custom mapping file) is currently not supported.

Missing features & bugs

During tests, I noticed that quite a few things are not supported, or don’t work properly:

  • Subquery support
  • Customised migration (only automatic migration works)
  • External storage for large binary files
  • Write-Ahead Logging
  • (#240) string comparison operators
  • (#241) some aggregate functions – average, sum, etc.
  • query with nested count, i.e comments.@count > 5 works, but author.comments.@count > 5 doesn’t

Tools

Below, a few tools for managing SQLCipher databases, which support Encrypted Core Data just fine:

DB Browser for SQLite. Open source database manager, gives the possibility to look up tables, stored data. Entering password is required to inspect encrypted databases. SQLCipher support is available as a separate binary download.

SQLiteManager. A similar, but proprietary tool. Promoted by the creators of SQLCipher.

Summary

Overall, it’s hard to say if Encrypted Core Data is a good choice for your app. Using it would definitely require some sacrifices, regarding user’s experience, or development speed.

While performance problems may be concerning, missing features aren’t that critical. I encourage you to try the library and maybe submit Pull Request, for stuff that you need.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts