源代码和基础设施完整性
签署你的代码
在制造业中,为生产订单提供物料清单(BOM)是一种常见做法。BOM 是用于制造最终产品的原材料、子组件、中间组件、子组件和零部件的列表。软件中也有类似的东西——软件物料清单(SBOM),但它仍然不太常见。
SBOM(软件物料清单)
如果你仔细看看近年来的软件供应链攻击,比如事件流(Event-Stream)事件(见 Thomas Claburn, 2018),你会发现攻击者通过在发布中注入恶意代码,使得 GitHub 中的源代码与 npm 包中包含的文件不匹配。SBOM 在此时可以帮助进行取证,并用于比较不同版本的哈希值。
在 SolarWinds 攻击中(见 Crowdstrike 博客,2021),依赖项并未被篡改。相反,攻击者在 SolarWinds 的构建服务器上运行了一个额外的进程,操控了文件系统,伪造了 MsBuild.exe 执行过程中的文件。为了帮助防止并调查此类攻击,你需要扩展 SBOM,涵盖构建过程中的所有工具以及构建机上运行的所有进程。
有几种常见的 SBOM 格式:
-
软件包数据交换(SPDX):SPDX 是一个开放标准,由 Linux 基金会发起,最初用于许可合规,但也包含版权、安全引用和其他元数据。SPDX 最近被批准为 ISO/IEC 标准(ISO/IEC 5962:2021),并且符合 NTIA 的最小元素要求。
-
CycloneDX(CDX):CDX 是一个轻量级的开放源代码格式,起源于 OWASP 社区,优化了 SBOM 生成过程与发布管道的集成。
-
软件标识(SWID)标签:SWID 是一个 ISO/IEC 行业标准(ISO/IEC 19770-2),被各种商业软件供应商使用。它支持软件清单自动化、评估机器上软件漏洞、检测缺失的补丁、配置检查、软件完整性检查、安装和执行的白名单/黑名单以及其他安全和操作性用例。它是对构建机上安装的软件进行清单管理的好格式。
每种格式都有不同的工具和使用案例。SPDX 可以由 Syft 生成。你可以使用 Anchore SBOM Action(见 [https://github.com/marketplace/actions/anchore-sbom-action](https://github.com/marketplace/actions/anchore-sbom-action))为 Docker 或 OCI 容器生成 SPDX SBOM:
- name: Anchore SBOM Action
uses: anchore/sbom-action@v0.6.0
with:
path: .
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}
SBOM 会作为构建工件上传(见图15.2)。

FOSSology(见 [https://github.com/fossology/fossology](https://github.com/fossology/fossology))是一个开放源代码许可合规解决方案,也使用 SPDX。
CDX(见 [https://cyclonedx.org/](https://cyclonedx.org/))更专注于应用安全性。市场上有适用于 Node.js、.NET、Python、PHP 和 Go 的版本,更多语言通过 CLI 或其他包管理器(如 Java、Maven 和 Conan)也能支持。用法很简单,下面是适用于 .NET 的 action 示例:
- name: CycloneDX .NET Generate SBOM
uses: CycloneDX/gh-dotnet-generate-sbom@v1.0.1
with:
path: ./CycloneDX.sln
github-bearer-token: ${{ secrets.GITHUB_TOKEN }}
SBOM 不会自动上传,你需要手动执行:
- name: Upload a Build Artifact
uses: actions/upload-artifact@v2.3.1
with:
path: bom.xml
CDX 还用于 OWASP Dependency Track(见 [https://github.com/DependencyTrack/dependency-track](https://github.com/DependencyTrack/dependency-track)),这是一个组件分析平台,可以作为容器或在 Kubernetes 中运行。你可以将 SBOM 直接上传到 DependencyTrack 实例:
uses: DependencyTrack/gh-upload-sbom@v1.0.0
with:
serverhostname: 'your-instance.org'
apikey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectname: 'Your Project Name'
projectversion: 'main'
SWID 标签更多用于软件资产管理(SAM)解决方案,如 Snow(见 [https://www.snowsoftware.com/](https://www.snowsoftware.com/))、Microsoft System Center 或 ServiceNow ITOM。CDX 和 SPDX 如果存在 SWID 标签,也可以使用它们。
如果你想了解更多关于 SBOM 的信息,请访问 [https://www.ntia.gov/sbom](https://www.ntia.gov/sbom)。
如果您完全在 GitHub Enterprise Cloud 上工作并使用托管的运行器,那么 SBOM 并不那么重要,因为所有相关数据都已经在 GitHub 上连接好了。但是,如果您使用 GitHub Enterprise Server、拥有自托管的运行器,并且在发布管道中使用了其他未由公共包管理器消费的商业软件,那么为所有发布生成 SBOM 可以帮助检测漏洞、许可证问题,并在发生事件时为取证提供帮助。
签署你的提交
我经常讨论一个问题:是否应该签署所有提交。Git 是非常强大的,它允许你修改现有的提交。但这也意味着,提交的作者不一定就是提交代码的人。一个提交有两个字段:作者(author)和提交者(committer)。这两个字段会根据 git config 中的 user.name 和 user.email 以及时间戳来设置。例如,如果你进行 rebase,提交者会变为当前的值,但作者会保持不变。这两个字段与对 GitHub 的认证完全没有关系。
你可以在 Linux 仓库中查找 Linus Torvalds 的邮箱地址,将本地 Git 仓库配置为使用该邮箱地址,然后提交到你的仓库。提交会显示为 Linus 的作者(见图15.3)。

但该提交不会像你通过 Web UI 修改文件或使用拉取请求合并更改时那样显示“已验证”徽章。已验证徽章表示该提交是用包含你账户验证邮箱地址的 GNU 隐私保护(GPG)密钥签名的(见图15.4)。

你可以在本地创建 GPG 密钥,并用它签署提交(git commit -S
)。当然,你可以自由地在密钥中设置一个名称和邮箱地址,只要它们与 git config 中配置的邮箱和用户名匹配。只要你没有修改该提交,签名就有效(见图15.5)。

即使你将 Pretty Good Privacy(PGP)密钥上传到 GitHub 个人资料(见 [https://github.com/settings/gpg/new](https://github.com/settings/gpg/new)),该提交也不会被验证,因为 GitHub 会查找与你帐户的验证邮箱地址匹配的密钥(见图15.6)。

我认为不必强制所有提交都要签署。问题在于,强制开发者签署每一个提交会拖慢进度。许多 IDE 和工具并不支持签署。保持密钥同步,处理多个邮箱地址——这一切都变得更加麻烦。如果所有开发者都在公司设备上使用相同的邮箱地址,这个方法可能适用。但通常情况下,并不是这样。人们远程工作、使用不同的机器,在不同的环境中工作,还在同一台机器上做开源软件时使用不同的邮箱。
因此,强制签署所有提交的好处并不值得。如果攻击者有权限推送代码到你的仓库,那么你最担心的应该不是伪造的邮箱地址。
-
选择一个依赖于拉取请求的工作流程,在服务器上合并、更改或重基(rebase)这些更改,以便它们默认签署。
-
如果你需要确保发布的完整性,签署你的标签(
git tag -S
)。由于 Git 基于 SHA-1 或 SHA-256 的树结构,签署标签将确保所有父提交没有被修改。
与其要求开发者签署所有提交并拖慢团队速度,不如在构建过程中签署代码,以确保没有人在构建后篡改你的文件。
代码签名
代码签名是指对二进制文件进行签名,即使你签名的是二进制文件而非源代码。为了进行代码签名,你需要从受信任的认证机构获取证书。如何在构建过程中签署代码,取决于你使用的编程语言和代码的编译方式。
要在 GitHub Actions 中为你的 Apple XCode 应用进行签名,你可以参考此文档,安装 base64 编码的证书和发布配置文件: [GitHub Actions 中安装 Apple 证书进行 Xcode 开发](https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificateon-macos-runners-for-xcode-development)。 如果你使用的是自托管的 runner,请记得在构建完成后清理证书,以免其他团队访问时产生安全风险。而在 GitHub 托管的 runner 上,每次构建都会从干净的环境开始。
根据你的代码签名解决方案,你可以在 GitHub Marketplace 中找到多种 Authenticode 和 signtool.exe 的操作。由于所有签名解决方案都是基于命令行的,你可以像 Apple 示例那样,将签名证书通过秘密上下文传递给你的工作流。