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.
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.
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
.gitignorefile - the secrets xcconfig must never be committed/pushed to git.
- Add your secrets to
secrets.xcconfigas key-value pairs, for example
MY_SECRET=my real secret
- In your
<configuration type>.xcconfigfile import
- In your app
Info.plistcreate a key
MySecretset to value
At build time
my real secret will be injected into the plist as the value of
Your file snippets should look similar to:
MY_SECRET = my real secret
<?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>
Note: The above plist snippet only shows one key for clarity
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.
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.
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.