In iOS, how can I store a secret "key" that will allow me to communicate with my server?

18,535

Solution 1

Crazy as it sounds, this is probably the best solution. Everything else is more complicated, but not much more secure. Any fancy obfuscation techniques you use are just going to be reverse engineered almost as quickly as they'll find this key. But this static key solution, while wildly insecure, is nearly as secure than the other solutions while imposing nearly no extra complexity. I love it.

It will be broken almost immediately, but so will all the other solutions. So keep it simple.

The one thing that you really want to do here is use HTTPS and pin your certificates. And I'd pick a long, random key that isn't a word. Ideally, it should be a completely random string of bytes, stored as raw values (not characters) so that it doesn't stand out so obviously in your binary. If you want to get crazy, apply a SHA256 to it before sending it (so the actual key never shows up in your binary). Again, this is trivial to break, but it's easy, and won't waste a lot of time developing.

It is unlikely that any effort longer than an hour will be worth the trouble to implement this feature. If you want lots more on the topic, see Secure https encryption for iPhone app to webpage and its links.

Solution 2

By hardcoding the string in your app, it's possible for attackers to decrypt your binary (via tools like dumpdecrypt) and get your string without much trouble (a simple hexdump would include any strings in your app).

There are a few workarounds for this. You could implement an endpoint on your REST API which returns your credentials, that you could then call on launch. Of course, this has its own non-trivial security concerns, and requires an extra HTTP call. I usually wouldn't do it this way.

Another option is to obfuscate the secret key somehow. By doing that, attackers won't be able to instantly recognize your key after decryption. cocoapods-keys is one option which uses this method.

There's no perfect solution here – the best you can do is make it as difficult as possible for an attacker to get a hold of your keys.

(Also, be sure to use HTTPS when sending requests, otherwise that's another good way to compromise your keys.)

Solution 3

While in-band tokens are commonly used for some schemes, you're probably eventually going to implement TLS to protect the network traffic and the tokens. This as Rob Napier mentions in another reply.

Using your own certificate chain here allows the use of existing TLS security and authentication mechanisms and the iOS keychain, and also gives you the option of revoking TLS credentials if (when?) that becomes necessary, and also allows the client to pin its connections to your servers and detect server spoofing if that becomes necessary.

Your own certificate authority and your own certificate chain is free, and your own certificates are — once you get the root certificate loaded into the client — are just as secure as commercially-purchased certificates.

In short, this certificate-based approach combines encryption and authentication, using the existing TLS mechanisms.

Share:
18,535
TIMEX
Author by

TIMEX

Updated on June 30, 2022

Comments

  • TIMEX
    TIMEX almost 2 years

    I want to store a secret key ("abc123") that I will use in the header of my REST API requests. My server will check this secret key. If it matches "abc123", then allow the request to be made.

    I'm thinking about a simple solution like:

    let secret = "abc123" 
    

    But are there going to be any downfalls to this?

  • TIMEX
    TIMEX about 9 years
    What about cocoapods-keys? Is that still breakable? Can you show me an example of how to create a raw value string in swift?
  • Rob Napier
    Rob Napier about 9 years
    I don't see much obfuscation benefit to cocoapods-keys. It's too simple and automatically reversible. The only thing obfuscation has going for it secrecy (which is why it's often so weak). If you open source your obscurity, you don't have anything left but hope.
  • Rob Napier
    Rob Napier about 9 years
    By raw bytes I just mean a simple C array of byes. At least it won't pop out from "strings."
  • zaph
    zaph about 9 years
    Please explain: "once you get the root certificate loaded into the client". Do you mean just into the app, into the iOS CA list or something else?
  • Rob Napier
    Rob Napier almost 9 years
    I believe Stephen is describing a form of certificate pinning, so only your app needs your root certificate, and your app only trusts your root certificate.
  • pete
    pete almost 7 years
    How is this preferable to simply doing nothing? It's the same as just having a hard-to-guess url right? Shouldn't the correct key should at least differ from request to request?
  • Rob Napier
    Rob Napier almost 7 years
    If you connect to your server over HTTPS, then a hard to guess URL is identical to this solution since the URL is encrypted the same way the header is. I don't know what you mean by "differ from request to request," but if you mean that you can embed some different key in each endpoint URL, then yes, that'd likely be a slightly better solution than this one. (Remembering we're talking about the difference between "almost useless" and "almost useless but ever so slightly less so.")
  • Radu Simionescu
    Radu Simionescu over 6 years
    At any moment, the server is assuming that the app is trusted to receive secret information and it has no actual way of verifying the app identity - it can be a hacked clone. You would need an app secret delivered with your app. Or an app secret generated for your app by the OS, and signed with an OS wide key to attest the generated key. But that would require the OS to have a secret key in every device. So the same problem: storing keys on client side in a tamper proof way. There is special hardware for that but not yet adopted on every device.
  • sweepez
    sweepez over 6 years
    @RobNapier not knowing much C not sure what you mean by array of bytes. Would an Array<UInt8> in swift be the same?
  • Rob Napier
    Rob Napier over 6 years
    Generally it's Data, but yes, [UInt8] achieves the same. The point is not to turn it into Base64 or anything like that. Make sure it's "just bytes" and not something that is UTF-8 encoded.
  • sweepez
    sweepez over 6 years
    @RobNapier thanks, could I get your advice on what I am trying to achieve. I need to store in source code the encryption key to a Realm database for a 100% offline app. The encryption key when read/loaded by the realm instance is 64-byte Data, but I am not sure how to store in code just Data. Thats why I mentioned I mentioned [UInt8] which is what I am currently going to use.
  • aguilarpgc
    aguilarpgc over 5 years
    By hardcoding the string in your app, it's possible for attackers to decrypt your binary This only could happen on a rooted ios device right?
  • Leo
    Leo almost 5 years
    This is a horrible solution. It's almost like giving away your user's sensitive data to attackers. Better alternatives are OIDC's PKCE or even Authorization Code flow where authorization happens in the server through a secured HTTPS channel
  • Tres
    Tres almost 5 years
    @aguilarpgc No, you can grab the binary from any iOS device.
  • Alexander
    Alexander almost 5 years
    @RobNapier I think this answer makes sense in the context of something like an API key, where one-way or another the client needs it, in plain-text, in order to function. Whether its shipped in the app bundle, or transmitted securely by a trusted server, it ends up being accessible to the app (and therefore the device owner) in plain text at one point or another (otherwise it wouldn't be usable). But you're answer is totally incorrect for encryption keys for communication, where key exchange mechanisms like Diffie Hellman exist.