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 exampleMY_SECRET=my real secret
- In your
<configuration type>.xcconfig
file importsecrets.xcconfig
- In your app
Info.plist
create a keyMySecret
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
<configuration type>.xcconfig
#include "secrets.xcconfig"
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>
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.
Top comments (2)
A good way not to commit your secret file is to create it in your parent folder, or any folder outside of your git repo.
I think that would be a nice enhancement - thanks!
Maybe by default it could get saved to a hidden file
~/.<module>-secrets.xcconfig