[译]在 Git 中保护 iOS 项目的敏感信息

2,670 阅读4分钟

原文链接 (自备梯子)

一些可以保护API key等信息的简单方法

几乎所有 iOS 应用都需要使用一些私有值,比如 API key,HMAC secrets 或密码。有一个简单的途径可以把这些私有值包含到你的 app 中,那就是把它们写在代码里或者写在 Info.plist 文件里。但是这么做会产生一个问题,就是这些文件会被包含到 Git 仓库里,任何人都可以看到并获取到源代码,自然也就可以获取到这些敏感信息。

保护源代码

针对上面提及的问题,首先想到的解决方式可能很简单,就是直接保护源代码,这样其他人就没法获取到源码,也就没办法获取到这些敏感信息了。然而不幸的是很多应用是开源的,每个人都需要去获取其源码,所以这种解决方式并不总是奏效的。对于一些顶级的 iOS app,人们还可以去反编译它或者分析应用包的 Contents 来寻找这些敏感信息。

让文件不包含到 Git 仓库中

在允许保持代码开源的前提下,你可以简单地让包含敏感信息的文件被 Git 忽略。任何人需要在项目中使用这些敏感信息,可以简单地创建相关文件。对于一个开源项目来说,这些相关细节,可以在 README 或者 wiki 页面里注明。

如果按照上面所说,那么一个 iOS 应用,每一个开发者都需要创建一个源代码文件或者一个 plist 文件,然后添加到项目中。这样你不得不很小心地在项目设置里去确保相关文件的引用情况,即使当这些相关文件不包含在 Git 中。

xcconfig

我更倾向于把敏感信息保存在 xcconfig 文件中,每一个编译环境( Debug 或 Release )对应一个文件。这些文件可以让你在不同环境使用不同的配置,为开发者简化配置,而且让敏感信息不包含在 Git 仓库里。

1. 创建一些样例文件在项目里

我更喜欢把它们放在 BuildConfig 文件夹里,让它们与项目里的其他文件分离开。开发者只需要简单地复制它们,给它们重命名,然后把一些敏感信息写到里面。这些样例文件可以指定 key 的列表,但是不把值包含进去。每一个编译版本的配置,比如 Debug 和 Release,都需要这么一个文件。

debug.example.xcconfig
API_CLIENT_ID = API_CLIENT_ID_FOR_TEST
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_TEST
release.example.xcconfig
API_CLIENT_ID = API_CLIENT_ID_FOR_PRODUCTION
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_PRODUCTION

你将需要把这些文件添加到 Xcode 项目中。但是你要确定把他们从 Xcode 的文件检查器的 Target Membership 区域中移除。

2. 为每一个编译版本创建真实文件

在这里面把例子的值替换成项目里真实的值

debug.xcconfig
API_CLIENT_ID = 123456789
API_CLIENT_SECRET = abcdefgh
release.xcconfig
API_CLIENT_ID = 987654321
API_CLIENT_SECRET = hgfedcba

你将要把这些文件添加到项目中,但是它们不需要依附到任何 target。

3. 把这些配置文件添加到 .gitignore 文件中。

4. 在项目设置里选中这些文件。

你需要在 Info->Configurations 中选中这些文件,在 Debug 和 Release 中都去设置它们。

值的使用

任何包含在 xcconfig 文件的值都可以作为项目文件的环境变量来使用,比如 Info.plist,如果需要再代码里使用这些值,你需要在 Info.plist 创建一个属性链接到环境变量,使用这种格式 $(API_CONFIG_KEY)

2018-02-01 更新

把敏感信息保存在Info.plist 不是最安全的方式。如果你的应该比较注重安全性,那么你可能需要把这些敏感信息放在其他地方。本篇文章的侧重点事把它们排除在 Git 仓库之外。后面我会再更新说明如何在代码中使用这些环境变量,来作为 Info.plist 的代替方案。

2018-02-03 更新

使用 Sourcery,可以生成一个 Swift 结构体,该结构体包含了在 xcconfig 文件里的特定敏感信息。

  1. Sourcery 添加到项目中。

  2. 创建一个 stencil 文件,例如 AppSecrets.stencil,把项目代码里需要访问到的敏感信息都写在这里,作为Swift常量。

struct AppSecrets {
    static let secretKey="{{ argument.secretKey }}"
}
  1. 添加 build phase 来生成 AppSecrets.swift。
Tools/Sourcery/bin/sourcery --sources Sources --templates Templates/AppSecrets.stencil --output Generated --args secretKey=\"$SECRET_KEY\"
  • Sourcery 的路径取决于是怎么安装它的,我这例子里是作为一个独立版本工具,将它包含在项目中。
  • 上面那个 sources 参数是必须的,虽然在这种情况下并没有用到,只是在后面输入了一个在项目里的有效路径。
  • 上面 templates 参数是 AppSecrets.stencil 相对于 root 的路径。
  • output 文件夹是 AppSecrets.swift 写入的地方,这个文件夹必须有。你可以在 build phase 添加 mkdir -p Generated 来确保这个文件夹存在。
  • 写这些参数的时候,他们分开的格式应该是这样:arg1=one,arg2=two 。我避免在 SECRET_KEY 内容里添加任何特殊字符。
  1. 把 AppSecrets.swift 加入到 .gitignore 文件中,让它不被添加到 Git 仓库中。
  2. 编译项目,然后把 AppSecrets.swift 加到项目中,这样 AppSecrets.swift 会被编译和链接到项目的 target 中。

注意:如果你的 App key 之类的敏感信息没有要求在不同的编译条件下( debug 和 release) 具有不同的值,你可以跳过 xcconfig 部分。你可以使用另一个方案,那就是把敏感信息写成一个可执行的 shell 脚本,添加到 build phase 中。

总结一下,通过以上这些方法,你可以让你项目中的敏感信息不被加入到 Git 仓库里,与此同时,对于这个项目新的开发者们来说,也比较易于理解上手。