Secure (xc)config for iOS apps

donniejp

donnie-jp

Posted on July 26, 2020

Secure (xc)config for iOS apps

App configuration dilemma šŸ˜Ÿ

Developers often have to set some app config (API secrets for Staging, hard-coded users/passwords) locally that they don't want to ever be committed to their git repo, especially if that repo is open source. But people (and developers are people too) can, and do, make mistakes. So you may end up having to delete PRs, clean git histories, and ask GitHub support staff - who are super helpful actually - to delete PRs and run git garbage collection etc., which is all stuff that takes time away from more enjoyable tasks like feature development or writing blog posts.

It would be very nice if the app config could be set in a way that it's impossible to accidentally push that supposed-to-be-local temporary confidential information to git. We have a full implementation of secure configuration in our Remote Config iOS SDK. In this post I will introduce the general approach.

plists and xcconfig āš™ļø

Many SDKs (including ours) require app developers to add key-value configs to their Info.plist and the SDKs read those values at runtime.

Instead of using hard-coded values in the plist we can make them variables e.g. $(API_KEY) that reference the real value stored in an xcconfig file. This allows the app to easily have different configs for their different environments. But, importantly, it also gives us a mechanism to remove secrets from the Info plist. Thanks are due to my colleague Pierre from the Mini App team for initiating this idea.

To understand the ins and outs of xcconfig files I followed this excellent Thoughtbot blog post which describes how to use xcconfig files to configure your app for different environments. Although I don't want to cover the same stuff I want to highlight this point from the author

Since these files will potentially contain secure information, such as API_KEY, Iā€™d recommend not checking them into version control and instead using a secure file storage system like 1Password

An alternative approach is to use a hidden-from-git "secrets" xcconfig file.

Make it secure šŸ”’

You can make the approach secure by following these steps:

  • Create a hidden-from-git secrets.xcconfig (or any name you prefer). The secrets xcconfig is hidden from git by adding it to your .gitignore file - the secrets xcconfig must never be committed/pushed to git.
  • Add your secrets to secrets.xcconfig as key-value pairs, for example MY_SECRET=my real secret
  • In your <configuration type>.xcconfig file import secrets.xcconfig
  • In your app Info.plist create a key MySecret set to value $(MY_SECRET)

At build time my real secret will be injected into the plist as the value of MY_SECRET.

Your file snippets should look similar to:

secrets.xcconfig

MY_SECRET = my real secret
Enter fullscreen mode Exit fullscreen mode

<configuration type>.xcconfig

#include "secrets.xcconfig"
Enter fullscreen mode Exit fullscreen mode

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>MySecret</key>
    <string>$(MY_SECRET)</string>
</dict>
</plist>
Enter fullscreen mode Exit fullscreen mode

Note: The above plist snippet only shows one key for clarity

Automate! šŸ¤–

The above approach works pretty well but it would be a pain to maintain. Our team deploy and integrate our SDKs using CocoaPods so I wanted to find a way to automate secrets management that fits our process. So what I did was add a CocoaPods post_install hook to our app Podfile that runs a custom shell script that generates the secrets xcconfig file from local environment variables. When the script runs during pod install we print a warning/error message if a required environment variable is not set.

Are there limitations? šŸ¤”

Security

This approach only prevents secrets in Info.plist files from being inadvertently committed to your git repo. It does not provide any run-time protection for your secrets and it should be noted that extracting secrets from an iOS ipa is as simple as unzipping the binary package and opening the plist in a text editor. If your secrets also need run-time protection you must consider an alternative approach.

Usability

The steps to add a new secret plist variable are not that obvious and it would be better to have a proper CocoaPods plugin. It could be based on the cocoapods-keys plugin (which stores secrets in the macos keychain and writes obfuscated secrets to generated source files that can be imported and used by app source files). I think making a plugin would be the logical next step for our secure xcconfig approach and I hope to explore building a plugin for this in future.

šŸ’– šŸ’Ŗ šŸ™… šŸš©
donniejp
donnie-jp

Posted on July 26, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related