App Security


  • Security is not a set of methods, it’s a system. It’s better to think about threats and trust as early as you can.
  • Avoid plaintext at all cost. Encrypt your communication. Encrypt your local data as required.
  • If you don’t store the password, it can’t be leaked. ;)
  • But if you need to store passwords, do it right!
  • It’s not paranoia if it’s true.

Actual reality: nobody cares about his secrets. (Also, I would be hard-pressed to find that wrench for $5.)

Keeping your user data safe and private is not the simplest of tasks. There is no golden solution; there’s no sure proof way of preventing user data from being stolen. If someone has the means and really wants to, the system will be compromised.

Two people are walking in the woods, they spot a bear. Security is not outrunning the bear. It’s outrunning the other person.

With that bleak image in mind, let’s see what we can do, to make it either as hard as possible, or as hard as feasible, for data to be breached.

Sure, security is hard, and there’s no bulletproof solution. But there’s a lot of small things we can still do, that significantly improve our chances of keeping the data safe.

Take a step back

The first thing we should think about is if we need to store that data at all. We’re often storing data we don’t need, out of habit or because of external pressures. Is it crucial that we store the user’s address? Their phone numbers? Their full names? What kind of genitalia they have? Sometimes that answer is yes, but most of the time, it’s no. Remember: the more data you have, the more appetising your dataset becomes, and the more you have to work to secure it.

Once we figured out what data is really needed and what data can be discarded, the next step is figuring out risk and threat models. What are the most vulnerable points? What is more likely to happen? What can’t be prevented?

There’s no point in making a connection super secure if the private key is kept locally in plaintext, in a file called myprivatekey.

Security is always a trade-off, between convenience and safety, between the effort you can put in and the risk level you face.

Start Early

There’s a very simple reason why iOS and the iPhone are so much more secure than Android. One of them was conceived with privacy and security in mind; the other wasn’t.

It’s much easier to build something with security in mind from the start than to build something and then try to tack some security onto it.

Sometimes, making something that is already built secure might mean having to deconstruct the whole thing and put it back together again. Sometimes not even that can help.

Never roll your own crypto

Cryptography is not easy. It’s not simple to conceive it, it’s not easy to implement it, and it’s not easy to test it. There are a plethora of examples of really good cryptographic tools being broken all the time. Because computers become more powerful over time, because of bugs that weren’t caught before. Tools will fail you, all you can do is try to make them have the least possible impact on your data.


For instance, if you’re using ephemeral keys for your communications, even if a tool is cracked or a key is leaked, the damage will be minimal, compared to using the same key for everything. Think of using ephemeral keys as not re-using your passwords.

Storing keys and tokens, embracing the Keychain

Plaintext is your enemy. Don’t store your keys as a variable in code. Don’t store it inside a local file. If something is sensitive, use the Keychain. Although it has a quite unfriendly API, there are good wrappers out there.

// Don't do this:
let mySecretPassword: String = "Super secret password, tell nobody!"

// Try this instead:
let mySecretPassword: String = keychainWrapper.string(for: passKey)

It’s a small step that already improves things by quite a lot.

Do you want to know more about key management? What goals and features should a key management system have, how to generate & store keys, should we obfuscate or encrypt keys? Take a look at @vixentael‘s last talk here.

Consider pinning your SSL certs and its public keys. Many iOS libraries will provide you with ways of doing SSL certificate pinning, but you probably want to pin the certificate’s public key instead, since pinning the SSL certificate itself means that if they are revoked or expired, your app might not work until you update the binary with new certificates. As with most things security, you’ll have to weight the pros and cons of each solution. Sometimes it makes sense to give away a bit of extra security in the name of user convenience; sometimes it doesn’t.

Just remember: if it’s too user-hostile, they won’t use it. The worst thing you can do is making your users fight your system, instead of embracing it.

You’ll find more information on pinning certificates and public keys in this great OWASP cheat sheet.

Plaintext is the enemy


Be it passwords, API keys, your local database, your cache, your users’ personal details, one thing remains true: plaintext is your enemy. It doesn’t matter how much care and effort you put into making your application secure, your server secure, your transport secure, if someone can just plug a phone, open iTunes and retrieve your certificates and keys, or even worse: lift them off from Github. Encryption is key.

Not a pretty picture.

How to easily and safely encrypt/decrypt user data

There’re many tools for symmetric and asymmetric encryption. Most of them are built on a top of existing community-proven libraries. We can name only a few, but there are more.

For example, Themis provides a good context-based model for encrypting and decrypting data. A context-based model uses not only a password but also a context, as a means of encrypting and decrypting data. We’ll be using the Secure Cell model for this.

// Firstly, create a TSCellSeal using a password. Remember to keep this password safe! Embrace the Keychain.
let masterKeyData = .utf8)
guard let cellSeal: TSCellSeal = TSCellSeal(key: masterKeyData) else {
    print("Error occurred while initializing object cellSeal")

    return nil

// Then we can encrypt some data.
// Now the context can be anything, we recommend you pick something that creates a secure hard-to-guess association to the data being de/encrypted. For example: it's database row number.
let encryptedMessage: Data
do {
    encryptedMessage = try cell.wrap( .utf8),
                    context: .utf8))
} catch let error as NSError {
    print("Error occurred while encrypting \(error)", #function)

    return nil

// Now to decrypt it, we need the same password as well as the same context.
let decryptedMessage: Data
do {
  decryptedMessage = try cell.unwrapData(encryptedMessage, context: .utf8))
} catch let error as NSError {
  print("Error occurred while decrypting \(error)", #function)

  return nil

Again, a complex model made simpler with Themis, but still quite complicated. Using ephemeral keys, using key derivation, all behind the scenes, abstracting the complexity of security.

SSL is not enough. Certificate Pinning is not enough.

Security cat I’m here to keep your data safe.

You’ve been reading a lot about the topic, and you feel confident that your app is well configured. It will only accept HTTPS connections. It pinned all the certificates for the critical stuff.

Unfortunately, SSL is a fragile system. (Remember, that story about WoSign, that issued certificates even if they’re not the domain owners?) Do not rely on SSL encryption if your app is dealing with sensitive data.

Add a layer of encryption for your network connection. Encrypt data before sending, decrypt after receiving. In the end-to-end world, a server wouldn’t even know what this data is.


There are a simple set of tools and concepts that can go a long way with minimal effort. While not all applications deal with bank transactions and therefore don’t need the utmost security levels, most applications deal with a lot of user data, and that data should be kept just as safe.

Don’t just give the illusion of security. Don’t give the illusion of insecurity either.


Read more