在iOS应用中管理机密

几乎所有iOS应用程序都需要私有值,例如API密钥,机密和密码。 其中一些可能需要在源代码中使用,以设置第三方SDK或后端API。 在构建过程中或使用开发人员工具时可能需要一些秘密,例如与Apple Developer帐户进行通信。

将秘密包含到应用程序中的简单方法包括:将值直接放入代码中,将值放入构建脚本中或将其保存在Info.plist文件中。 不幸的是,这些方法意味着这些秘密被提交给源代码控制,并且任何对代码有访问权限的人都可以看到。 此外,可以从已编译的应用程序中提取添加到应用程序捆绑包中的脚本和plist文件。

我们将研究一些更安全的方式来管理iOS项目中的机密,以便从代码中访问它们。 值得注意的是,由于应用程序在用户设备上“泛滥成灾”,因此几乎不可能将应用程序中的任何内容100%保密。 一个好的方法是针对这种情况实施适当的保护级别,例如,银行应用程序应该比待办事项列表应用程序具有更高的安全级别。

本文是对我之前撰写的关于在iOS应用程序中保持Git机密的主题的文章的改进和扩展。

出于各种原因,对于开放源代码项目和封闭源代码项目,都必须使秘密不受源代码控制。

  1. 通过将秘密提交给源代码控制,您将与所有对存储库具有读访问权限的用户(包括将来会访问任何权限的用户)共享这些值。 您可能拥有需要访问项目但不一定使用所有机密的用户。
  2. 用户签出存储库后,他们将获得存储在本地计算机上的所有机密的副本。
  3. 如果您的源代码控制系统受到威胁,则不仅将获得源代码,还将获得所有密码和其他机密。

Git允许我们包含一个.gitignore文件,在这里我们可以列出应保留的文件(或表达式)。 如果我们的应用程序需要这些文件,则每个开发人员只需创建这些文件并添加任何必需的值即可。 我们可以在Git中包含这些文件的模板版本,或在文档中提供所需指南,包括在需要时提供秘密的人员的联系方式。

大多数iOS开发人员都知道,许多iOS项目使用Cocoapods作为依赖项管理器。 Cocoapods包括一个插件系统,该插件系统可将其进程连接到其中。 cocoapods-keys是可用于安全管理应用程序秘密的此类插件之一。

cocoapods密钥不仅可以将秘密信息排除在项目源之外,还可以将其安全地保存在系统钥匙串中。 安装或更新Pod后,会要求开发人员提供每个尚未存储的值的密钥。 生成一个Objective-C类,其中包含键的混淆版本及其值。 由于此类是通过运行Cocoapods构建的,因此Pods/CocoaPodsKeys目录可以添加到.gitignore

该插件具有许多优点:

  • 它要求为每个密钥提供一个值,而无需记录所需的秘密。 这使得每个开发人员都很容易配置他们的项目环境。
  • 源文件已生成,因此可以很容易地使其不受源代码控制。
  • 机密会在生成的源中加密,以防止从应用程序二进制文件中提取密钥。
  • 可以从Swift和Objective-C源码中使用它。
  • 通过读取钥匙串,我们可以根据需要访问构建脚本中的秘密。
  • 我们可以在使用cocoapods-keys的不同项目之间共享密钥。

将cocoapods-keys集成到我们的项目中非常简单,并且首先将插件包含到我们的Podfile

在下一次运行pod install ,将要求我们输入上面列出的每个键的值。 配置完所有密钥后,插件将生成一个源文件,可以在我们的代码中引用该源文件来读取密钥。

对于像后端API秘密这样的值,我们可能希望它在调试和发布时有所不同,我们可以同时包含两个密钥,然后在运行时读取另一个密钥。 显然,有许多不同的处理方式,具体取决于我们拥有的用例。 因此,我们在这里不讨论所有可能的选项,而仅根据应用程序是否在调试配置中构建而着眼于切换。

如果您在项目中使用Cocoapods,我建议您使用Cocoapods键来解决其他问题,因为它是一种安全且易于使用的方法。

许多项目不使用Cocoapods,或者我们可能不想使用插件,因此我们也可以考虑自己实施解决方案。 机密将被传递到构建过程中,在此过程中它们将作为环境变量提供。 然后,我们可以读取这些变量并使用它们来生成源文件,该文件将使我们的代码可以访问我们的秘密值。

我们可以在不同的环境中使用不同的值

Xcode提供了可以链接到特定构建配置的xcconfig文件,以便加载其中指定的设置。 可以为每个构建配置指定一个xcconfig文件,从而使我们可以为每个环境使用不同的值。 我们可能希望将我们的应用程序指向发布应用程序的生产API,但将其指向调试应用程序的测试后端。

请注意,如果某个特定的应用程序不需要依赖于构建配置的不同值,则可以使用Shell脚本而不是xcconfig文件,并在以后将其源文件输入到我们的最终构建阶段。

我们首先创建xcconfig文件的示例版本。 如果我们将这些文件放在诸如BuildConfig之类的目录中,它将使它们与其他项目文件分开。 建议将这些文件包括在项目中,以便它们出现在Xcode中,但我们应确保未将它们添加到File InspectorTarget Membership区域内的任何目标。

→debug.example.xccconfig

  CHAT_API_CLIENT_SECRET = CHAT_API_CLIENT_SECRET_TEST 
ANALYTICS_WRITE_KEY = ANALYTICS_WRITE_KEY

→release.example.xccconfig

  CHAT_API_CLIENT_SECRET = CHAT_API_CLIENT_SECRET_PROD 
ANALYTICS_WRITE_KEY = ANALYTICS_WRITE_KEY

通过复制和重命名示例文件,我们可以轻松创建将由项目使用的实际文件。 与示例文件一样,我们希望这些文件在项目中,但不附加到任何目标。

→debug.xccconfig

  CHAT_API_CLIENT_SECRET = 123456789 
ANALYTICS_WRITE_KEY = abcdefgh

→release.xccconfig

  CHAT_API_CLIENT_SECRET = 987654321 
ANALYTICS_WRITE_KEY = abcdefgh

为避免将真正的xcconfig文件添加到源代码管理中,应在.gitignore文件中列出它们。 我们可以在规则中使用正则表达式来捕获所有配置的文件。

  BuildConfig / *。example.xcconfig 

最后一步是告诉Xcode使用我们的xcconfig文件,该文件在项目文件的Info → Configurations下指定。 确保为每个构建配置和所需的目标选择一个xcconfig。

当Xcode生成您的项目时,xcconfig文件中的值可作为环境变量使用。 如果希望使用$(CHAT_API_CLIENT_SECRET)形式,则可以在Info.plist文件中简单地使用这些值。 我们已经讨论过,将机密放入plist文件中并不是很安全,但是为了完整性起见已经提到过。

我们将使用称为Sourcery的工具生成源文件,然后在我们的代码中引用该源文件以访问我们的机密。 不用说,我们首先需要将Sourcery添加到我们的项目中,例如在存储库中包含独立版本或使用Cocoapods。

Sourcery使用模板系统,我们在其中创建一个模板文件以显示该工具如何生成我们的代码。 我们将创建AppSecrets.stencil其中包含一些语法,以在从模板生成文件时替换秘密值。

接下来,我们需要通过选择项目文件,然后选择正确的目标并转到“构建阶段”选项卡,将构建阶段添加到我们的项目中。

  • Sourcery可执行文件的路径将取决于其安装方式。
  • 即使在这种情况下不使用sources参数,也需要指定它。 我们可以简单地将其指向我们的主要来源或任何有效目录。
  • templates参数是从项目根目录到模板文件的路径。
  • 输出目录是生成的源文件的写入位置。 我们可能需要通过在构建阶段开始时添加mkdir -p Generated来确保此文件夹存在。
  • 在args中,值用逗号分隔,形式为: arg1=one,arg2=two 。 如果机密值包含任何特殊字符,则最好按上述方法进行转义。

我们可以使用更复杂的脚本来代替在构建阶段中手动指定机密。 如果存在多个秘密,或者构建阶段难以维护,那么这可能是有益的。

添加构建阶段之后,我们可以正常构建应用程序,然后将生成的AppSecrets.swift文件添加到项目中,以便对其进行编译并链接到项目目标。 与xcconfig文件一样,我们应该将AppSecrets.swift添加到.gitignore文件中,以使其脱离Git。

在我们的代码中使用秘密就像引用我们生成的结构一样简单。

我们想要实现的目标是能够在源代码中引用秘密值,而无需将这些值本身保留在源代码控制中。 我们研究过的两个解决方案完全不同,但是都实现了相同的目标。

通过使用cocoapods键,我们可以避免手动设置,也可以避免将值以纯文本格式存储在项目中的任何位置。 但是,我们使用Sourcery的解决方案无需使用Cocoapods就可以使用,并且仍然需要很少的维护。 肯定会有更多的在线解决方案可用于更多用例,这将归结为针对情况使用最合适的解决方案。

您如何管理项目中的机密? 您是否还有另一种解决方案,相对于我们在这里所说的有什么优势? 如有任何问题或想法,或有其他任何疑问,请随时通过Twitter @lordcodes与我联系。

如果您喜欢阅读的内容,请随时分享文章并订阅我的供稿。

感谢您的阅读和愉快的编码! 🙏


彩色阅读代码始终是最好的方法,对吗? 您可以在Lord Codes上找到此文章,在那里您将突出显示语法! 🎉