如何安全地配置 CI 服务器以对二进制文件进行数字签名?

Posted

技术标签:

【中文标题】如何安全地配置 CI 服务器以对二进制文件进行数字签名?【英文标题】:How do I securely configure a CI server to digitally sign binaries? 【发布时间】:2014-11-19 17:02:15 【问题描述】:

有许多网站解释了如何在 .pfx 证书文件上运行 signtool.exe,归结为:

signtool.exe sign /f mycert.pfx /p mypassword /t http://timestamp.server.com \
  /d "My description"   file1.exe file2.exe

我有一个持续集成 CI 流程设置(使用 TeamCity),它与大多数 CI 流程一样,可以完成所有工作:检查源代码、编译、签署所有 .exe、打包到安装程序中以及签署安装程序 .exe。目前有 3 个构建代理,运行相同的虚拟机,其中任何一个都可以运行此过程。

不安全的实现

今天为了实现这一点,就安全性而言,我做了几件坏事(TM):.pfx 文件在源代码管理中,它的密码在构建脚本中(也在源代码管理中)。这意味着任何有权访问源代码存储库的开发人员都可以获取 pfx 文件并做他们想做的任何邪恶的事情。 (我们是一家相对较小的开发商店,相信每个人都可以访问,但显然这仍然不好)。

终极安全实施

我能找到的关于“正确”执行此操作的所有信息,就是您:

将 pfx 和密码保存在某个安全介质上(例如具有基于手指解锁功能的加密 USB 驱动器),并且可能不在一起 只指定几个人可以访问签名文件 仅在未连接的专用计算机上签署最终版本,该计算机被保存在锁定的保险库中,直到您需要将其带出用于此代码签名仪式。

虽然我可以看到这个安全性的优点.. 这是一个非常繁重的过程,并且在时间方面很昂贵(运行这个过程,安全地保存证书备份,确保代码签名机器处于工作状态状态等)。

我确信有些人会跳过步骤,直接使用存储在他们个人系统上的证书手动签署文件,但这仍然不是很好。

它也与随后在安装程序中使用的签名文件不兼容(这也是由构建服务器构建的) - 当您安装了具有 UAC 提示以获取管理员的已安装 .exe 时,这一点很重要访问。

中间立场?

比起证明它是我的公司,我更关心的是不向用户呈现可怕的“不受信任的应用程序”UAC 提示。同时,将私钥和密码存储在每个开发人员(加上 QA 和高级技术支持)都可以访问的源代码存储库中显然不是一个好的安全实践。

我希望 CI 服务器在构建过程中仍然像现在一样签名,但没有密码(或证书的私钥部分),每个有权访问源代码存储库的人都可以访问.

有没有办法让密码不被构建或以某种方式保护?我是否应该告诉 signtool 使用证书存储(以及如何使用 3 个构建代理和构建作为非交互式用户帐户运行)?还有什么?

【问题讨论】:

您使用的是哪个版本控制?在像 TFS 这样的集中式 VC 中,您甚至可以保护单个文件并将访问权限限制为仅限管理员。 Git。不过,这确实给了我一个想法——我也许可以将该文件存储在具有不同安全性的单独存储库中,并让 Teamcity 也对其进行检查。如果我没有得到任何更好的答案,我会用我最终做的任何事情来更新帖子。 【参考方案1】:

我最终采用了与@GiulioVlan 建议的方法非常相似的方法,但做了一些更改。

MSBuild 任务

我创建了一个执行 signtool.exe 的新 MSBuild 任务。此任务有两个主要目的:

它隐藏了密码,永远不会被显示 它可以在失败时重试时间戳服务器 方便调用

来源:https://gist.github.com/gregmac/4cfacea5aaf702365724

这专门获取所有输出并通过消毒功能运行它,用所有 * 替换密码。

我不知道审查常规 MSBuild 命令的方法,因此如果您在命令行上将密码直接传递给 signtool.exe,使用它将显示密码 - 因此需要执行此任务(除了其他好处) .

注册表中的密码

我讨论了几种“带外”存储密码的方法,最终决定使用注册表。从 MSBuild 访问它很容易,手动管理也相当容易,如果用户没有 RDP 和远程注册表访问机器,它实际上是相当安全的(有人能说别的吗?)。大概也有一些方法可以使用花哨的 GPO 东西来保护它,但这超出了我想要了解的范围。

这可以通过 msbuild 轻松读取:

$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\1 Company Dev@CodeSigningCertPassword)

而且很容易通过 regedit 进行管理:

为什么不在其他地方?

在构建脚本中:任何拥有源代码的人都可以看到它 在源代码控制中加密/混淆/隐藏:如果有人获得了源代码的副本,他们仍然可以弄清楚这一点 环境变量:在 Teamcity Web UI 中,每个构建代理都有一个详细信息页面,该页面实际显示所有环境变量及其值。可以限制对该页面的访问,但这意味着其他一些功能也受到限制 构建服务器上的文件:可能,但似乎更有可能通过文件共享或其他方式无意中访问它

从 MSBuild 调用

在标签中:

<Import Project="signtool.msbuild.tasks"/>

(您也可以将其与其他任务放在一个通用文件中,甚至直接嵌入)

然后,在您要用于签名的任何目标中:

<SignTool  SignFiles="file1.exe;file2.exe" 
   PfxFile="cert.pfx" 
   PfxPassword="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\1 Company Dev@CodeSigningCertPassword)"
   TimestampServer="http://timestamp.comodoca.com/authenticode;http://timestamp.verisign.com/scripts/timstamp.dll" />

到目前为止,效果很好。

【讨论】:

很好的解决方案,您甚至可以将 ACL 添加到注册表项并进一步限制对它的访问。 如果您在 Windows 上运行,您可以将证书导入证书存储。授予构建代理访问私钥的权限,通过名称或指纹指定要使用的证书。您甚至可以将密钥标记为不可导出。现在您只需要保护您的构建过程正在运行的用户帐户的密码(无论如何您都应该这样做)【参考方案2】:

一种常见的技术是将密钥和证书保留在版本控制中,但使用密码或密码保护它们。密码保存在机器本地的环境变量中,可以通过脚本轻松访问(例如%PASSWORD_FOR_CERTIFICATES%)。

必须注意不要以纯文本形式记录这些值。

【讨论】:

然后,当您签出旧版本来构建它时,您可能必须记住签出密钥的主干版本,否则您将针对过时的密钥进行签名......跨度> 我的建议是将密钥/证书保持在相同的存储库/文件夹结构中,除非您想设置更复杂的构建过程。请记住,私钥受密码保护,您可以有多个密码(例如 %Y2014_PASSWD% 和 %Y2016_PASSWD%)。 是的,我不想让构建结构变得复杂,但我也不想将我的签名密钥绑定到软件的当前版本,因为它们应该绑定的是当前日期.在许多方面,CI 系统没有解决这个问题的方法很糟糕。在 Jenkins 中,我可以通过私钥设置 SSH 凭据,因此我应该能够以几乎相同的方式设置应用程序签名凭据。我只是不知道如何为此编写插件。 仅仅因为某些东西很常见并不能使它变得聪明或安全。将私有证书密钥存储在源代码管理中是很疯狂的,即使它们受密码保护。

以上是关于如何安全地配置 CI 服务器以对二进制文件进行数字签名?的主要内容,如果未能解决你的问题,请参考以下文章

如何对对象数据的mongodb聚合数组进行分组以对同一日期的数字求和

如何在 PHP 中安全地返回文件数据

如何配置 ASP.NET Core 以对两个已部署的应用程序进行不同的配置

编码类型,如何确定[关闭]

CI框架中如何配置Nginx

如何在 Gitlab CI for android 中管理签名密钥库