What the hex?
Published January 30, 2015
We all know it's not a good idea to commit secret API keys into your git repository. For RubyMotion, it's tempting to do so anyway, since the code will be compiled into a binary.
This is a terrible idea.
Let me show you how easy it is to steal that information.
1. Spin up a RubyMotion app
2. Add an API key in your app delegate.
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) my_happy_api_key = "ABCDEFGHIJKLMNOPYAY" true end end
3. Build an IPA archive
$ rake archive `}
4. Open up that IPA file and extract it with Archive Utility
5. Find the binary
Go into the
Payload folder, right-click on the app and Show Package Contents. You should see something like this:
6. Download a hex editor.
I use Hex Fiend.
7. Open the binary in Hex Fiend
Choose File -> Open, then drag the binary from Finder into your file dialog, which will pre-select it. I use this trick all the time in OSX.
You'll see something like this:
Search for your API string
Command+F, choose "Text" on top left and paste in your string.
If you can find it that easily, someone else could scan through the binary and find it too.
It's even easier from Terminal. Just cd into that folder and run
strings my_happy_app | grep "YAY".
Aren't my apps protected by the App Store?
Not really. You don't even need to jailbreak your iPhone to useiExplorer and the various protections Apple puts around your app are relatively easy to circumvent.
You can't use a plist or NSUserDefaults, because those arequite vulnerabletoo. Even the Keychain is not entirely foolproof.
Your best option is probably obfuscation -- make it harder on the hacker. Here's a very simplistic example.
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) my_happy_api_key = set_up_items puts my_happy_api_key true end def set_up_items visibility_s("QUJDREVGR0hJSktMTU5PUFlBWQ" + "==" + " ") end def visibility_s(s) NSString.alloc.initWithData(visibility_d(s), encoding: NSUTF8StringEncoding) end def visibility_d(s) NSData.alloc.initWithBase64Encoding(s) end end
Note that I'm using odd method names here. You probably don't want
decode_api_base64_string as your method, since that will be clearly obvious to anyone looking at your binary.
Running that app correctly decodes the string:
But it doesn't appear in the binary:
This is by no means foolproof. Someone could see that string, deduce that it's a Base64-encoded string (especially by the method calls around it), and just run their own Base64 decoder on it. So you'll want to devise your own clever obfuscation strategies.
Have any other iOS API key security tips? I'd love to hear them. Hit me up on Twitter.
UPDATE: Ørta pointed out his awesomecocoapods-keys, which deals with this issue. Note that it requires a pre-release CocoaPods to install.
Ian Hofmann-Hicks and Ryan Linton for helping with this article. Also,Derek Selander's security tutorial on Ray Wenderlich(alsopart 2) is awesome.