37

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?

mfaani
  • 33,269
  • 19
  • 164
  • 293
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • http://stackoverflow.com/a/40423579/3141234 – Alexander Nov 04 '16 at 13:14
  • you did use the word secret key, but if you're sending as in the header, then what you mean is actually `api-key` not `secret-key`. Can you edit your question? – mfaani Jul 25 '19 at 22:40

5 Answers5

31

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.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • What about cocoapods-keys? Is that still breakable? Can you show me an example of how to create a raw value string in swift? – TIMEX Apr 22 '15 at 05:33
  • 3
    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 Apr 22 '15 at 10:32
  • 1
    By raw bytes I just mean a simple C array of byes. At least it won't pop out from "strings." – Rob Napier Apr 22 '15 at 10:32
  • 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? – pete Jul 10 '17 at 18:18
  • 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.") – Rob Napier Jul 10 '17 at 18:51
  • @RobNapier not knowing much C not sure what you mean by array of bytes. Would an Array in swift be the same? – sweepez Jan 07 '18 at 22:58
  • 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. – Rob Napier Jan 08 '18 at 01:30
  • @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. – sweepez Jan 08 '18 at 19:40
  • 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 – Leo May 14 '19 at 02:19
  • @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. – Alexander Jul 25 '19 at 21:52
6

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.)

Sam
  • 839
  • 10
  • 16
  • `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? – aguilarpgc Oct 16 '18 at 16:45
  • @aguilarpgc No, you can grab the binary from any iOS device. – Tres Jul 24 '19 at 15:15
2

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.

Community
  • 1
  • 1
Stephen Hoffman
  • 312
  • 2
  • 4
  • 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? – zaph Apr 25 '15 at 17:01
  • 1
    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. – Rob Napier May 08 '15 at 00:05
  • 1
    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. – Radu Simionescu Nov 22 '17 at 08:07
0

It looks like you are using access tokens. I would use Keychain for access tokens. For Client IDs, I would just keep them as a variable because client ids don't change while access tokens change per user, or even per refresh token and keychain is a safe place to store user credentials.

Gasim
  • 7,615
  • 14
  • 64
  • 131
0

I have used the PFConfig object (a dictionary) that allows you to retrieve in your app values of variables stored as Server environment parameters.
Similar to the environment variables that can be retrieved using ENV in web sites server side programming like Ruby or PHP. In my opinion this is about as secure as using Environment variables in Ruby or similar.

PFConfig.getConfigInBackgroundWithBlock{
      (config: PFConfig?, error: NSError?) -> Void in
      if error == nil {
        if let mySecret = config["mySecret"] as? String {
          // myFunction(mySecret)
        }

    }
lguerra10
  • 249
  • 3
  • 5