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

my_happy_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 `}

binary

4. Open up that IPA file and extract it with Archive Utility

extracting

5. Find the binary

Go into the Payload folder, right-click on the app and Show Package Contents. You should see something like this:

happy app

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:

hex fiend

Search for your API string

Command+F, choose "Text" on top left and paste in your string.

Uh-oh.

If you can find it that easily, someone else could scan through the binary and find it too.

uh-oh

It's even easier from Terminal. Just cd into that folder and runstrings my_happy_app | grep "YAY".

yay not

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.

Your Options

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.

base64

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 wantdecode_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:

decoded

But it doesn't appear in the binary:

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

H/T to
Ian Hofmann-Hicks and Ryan Linton for helping with this article. Also,Derek Selander's security tutorial on Ray Wenderlich(alsopart 2) is awesome.