概述
去年年底轰动一时的SolarWinds攻击事件以及今年4月曝出的针对Codecov产品代码的攻击使得供应链安全屡次成为安全界关注的焦点,针对此类问题谷歌提出了一种解决方案,以期确保软件包的完整性并防止未经授权的篡改。
这套解决方案名为Supply chain Levels for Software Artifacts(软件构件的供应链级别),简称为 SLSA。这个端到端的框架旨在确保软件开发和部署过程的安全性,专注于缓解由于篡改源代码、构建平台或构件仓库而产生的威胁。
SLSA的灵感来自于谷歌内部执行机制 Binary Authorization for Borg(BAB),这是一套验证代码来源并实现代码标识的审计工具,以确认部署的产品软件是否经过了适当的审查和授权。
本文将从 WHY、WHAT 、HOW 三方面展开了解 SLSA。
一、WHY
1.1 术语
- 构件(artifact)
构件代表不可变的数据块。比如:一个文件,一次git提交,一个文件目录(以某种方式序列化),一个容器镜像,一个固件镜像等。
- 软件供应链(software supply chain)
一系列创建构件的步骤。本文中将供应链表示为源、构建、依赖和包的有向无环图。此外源代码、构建和包都可以托管在一个平台上,例如源代码管理(SCM)或持续集成/持续部署(CI/CD)。请注意,一个构件的供应链是构件所依赖关系的供应链加上本身的源和构建过程的组合。
术语 | 描述 | 例子 |
源(Source) | 不经修改直接由人编写的构件。是供应链的起点 | Git提交(源)托管在GitHub(平台)上 |
构建(Build) | 将一组输入构件转换为一组输出构件的过程。输入可能是源、依赖项或临时构件输出 | Travis CI(平台)执行.travis.yml(进程) |
包(Package) | 发布”供他人使用的构件。在SLSA模型中,包始终是构建流程的输出。 | Docker镜像(包)在DockerHub(平台)上发布。 |
依赖(Dependency) | 作为构建过程的输入但不是源的构件。在SLSA模型中,它总是一个包。 | 在Alpine Linux(平台)上发布的Alpine package(包) |
Tips:包含源代码的ZIP文件是一个包,而不是一个源,因为它是从其他源构建而来的,比如git提交。
1.2 为什么需要 SLSA
SLSA致力于解决以下三个问题:
- 软件供应商希望确保他们的供应链安全,但不知道具体如何做。
- 软件消费者希望了解并限制他们可能遭受供应链攻击的风险,但却没有办法做到这一点。
- 单凭构件签名只能阻止潜在攻击中有限的一部分。
因此,SLSA可作为软件生产者和消费者的一套指导原则。更重要的是,SLSA允许通过一种通用语言讨论供应链风险和缓解措施。这将允许跨组织边界对这些风险进行沟通和采取行动。
一旦SLSA完成,软件生产者和消费者将能够通过查看软件构件的SLSA级别,了解到已经可防御哪些攻击。
二、WHAT
2.1 SLSA定义
构件的SLSA级别描述了其直接供应链的完整性强度,直接供应链的意思是构件的直接来源及构建步骤。为了验证构件满足此级别,需要provenance(出处)。这可以作为满足关卡要求的证据。
2.2 原则
SLSA非常注重以下两个原则:
- 非单方性
没有人可以在未经至少一个其他 “受信人 “明确审查和批准的情况下,在软件供应链的任何地方修改构件。其目的是预防、阻止和/或早期发现风险/坏的变化。
- 可审计
软件构件可以安全地、透明地追溯到原始的、人类可读的源和依赖项。其主要目的是自动分析源和依赖项。
虽然不可能杜绝所有问题,但这两个原则为广泛的篡改、混淆和其他类型供应链攻击提供了实质性的缓解。
谷歌开源安全团队的Kim Lewandowski和Borg团队的Mark Lodato表示:“在目前看来,SLSA是一套由行业共识建立的,并可逐步采纳的安全指导方针。”
2.3 SLSA Level
SLSA框架承诺端到端软件供应链的完整性,包括4个不同级别的渐进软件安全Level。SLSA 4是当前的最高水平,代表了理想的状态。SLSA 1-3提供较低的安全性保证,但更容易满足。实现SLSA 4可能需要很多年和重大的努力,所以中间的里程碑也是很重要的。
提示:以下定义当前尚未最终确定,可能会有变化,特别是SLSA 3-4。
2.4 Level要求
〇 代表除非有正当理由
以下是对上表的详细解释:
1、Source源:表示对构件的顶级源(top-level source)的需求,也包含构建脚本的源。
(1) 版本控制:对源代码的每个更改都在版本控制系统中进行跟踪,以识别出是谁做了更改,更改了什么,以及何时发生更改。
(2) 验证历史:每个历史更改都至少有一个强验证的参与者身份(作者、上传者、审阅者等)和时间戳。
(3) 无限期保留:构件及其变更历史被无限期保留,不能被删除。
(4) 两人审核:每一个历史改动,至少都有两个值得信任的人同意。
2、Build构建:表示构件在构建过程中的需求。
(1) 脚本:所有构建步骤都在某种“构建脚本”中完整定义。唯一的手动命令(如果有的话)是调用构建脚本。
(2) 构建服务:所有构建步骤都使用一些构建服务运行,比如持续集成(Continuous Integration, CI)平台,而不是在开发人员的工作机上完成。
(3) 临时的环境:构建步骤在临时环境中运行,例如容器或VM,那么这些环境仅为此构建提供,而不是从先前构建中重用。
(4) 隔离:在隔离环境中运行构建步骤,不受其他构建实例的影响,无论是先前的还是并发的。构建缓存(如果使用)纯粹是内容可寻址的,以防止篡改。
(5) 无参数:除了构建入口点和顶级源位置外,构建输出不能受到任何用户参数的影响。
(6) 封闭:所有构建步骤、源代码和依赖项都预先使用不可变引用完全声明,并且构建步骤在没有网络访问的情况下运行。所有依赖项都由构建服务控制平台获取并检查完整性。
(7) 可重现:使用相同的输入构件重新运行构建步骤会产生逐位相同的输出。 (无法满足此要求的构建必须要能提供理由)
3、Provenance 出处:表示对构件来源的要求。
(1) 可用:来源对构件的使用者或验证策略的任何人可用,并且它至少能标识构件、执行构建的系统和顶级源。所有构件引用都是不可变的,例如通过加密哈希。
(2) 已认证:可以验证出处的真实性和完整性,例如通过数字签名。
(3) 服务生成:Provenance 由构建服务本身生成,而不是由用户提供的工具在服务之上运行。
(4) 不可篡改:Provenance不可能被构建服务的用户伪造。
(5) 依赖完整:Provenance 记录了所有构建依赖项,构建脚本可用的每个构件,包括构建工作者的机器、VM 或容器的初始状态。
4、Common通用:供应链中涉及的每个可信系统(源、构建、分发等)的共同需求。
(1) 安全:系统满足一些待定的基线安全标准,以防止被利用。(修补漏洞、漏洞扫描、用户隔离、传输安全、安全启动、机器身份等。也许是 NIST 800-53 或其子集。)
(2) 访问:所有物理和远程访问都必须在多方批准后进行,要有记录和控制。
(3) 超级用户:只有少数平台管理员可以覆盖此处列出的保证。这样做必须需要第二个平台管理员的批准。
2.5 SLSA 范围
SLSA是不可传递的。它描述了构件构建过程和顶级源的完整性保护,但没有描述构件的依赖关系。依赖项有它们自己的 SLSA 评级,并且有从 SLSA 0 依赖项构建 SLSA 4 构件的可能。
非传递性的原因是为了使问题易于处理。如果 SLSA 4 要求依赖项也是 SLSA 4的,那么达到 SLSA 4 将需要从供应链的最源头开始推进。这反而是倒退,因为这将导致我们首先关注风险最小的部分,并会阻碍进一步的进展。通过使每个构件的 SLSA 评级相互独立,它允许基于风险的并行推进和优先级排序。(这是在整个 Google 中大规模部署其他安全控制时吸取的教训)
三、HOW
3.1 示例
下面将通过官方docker镜像使用curl的示例。展示在软件供应链中可能面临的威胁(选择curl仅仅是因为它是一个流行的开源包,而不是为了把它单独挑出来)。
这项工作需要大量的手工工作。
- 这里以Docker Hub中7.72.0 版本的curl为例;
2、Dockerfile来自GitHub中的curl/curl-docker仓库;
https://github.com/curl/curl-docker
3、Dockerfile中读取以下构件,假设在构建期间没有进一步的读取:
(1)Docker Hub镜像
registry.hub.docker.com/library/alpine:3.12.7
(2)Alpine 包
libssh2、libssh2-dev、libssh2-static、autoconf、automake、build-base、groff、openssl、curl-dev、python3、python3-dev、libtool、curl、stunnel、perl、nghttp2、brotli、brotli-dev
(3)文件
来源在https://curl.haxx.se/ca/cacert.pem
4、每个依赖项都又有它自己的供应链,来看一下curl-dev,它包含实际的“curl”源代码。
https://pkgs.alpinelinux.org/package/edge/main/x86/curl-dev
5、curl-dev包与其他所有 Alpine 包一样,在 Alpine git 仓库的 APKBUILD 中定义了其构建脚本。其中包括几个构建依赖项:
(1)文件
来源在 https://curl.haxx.se/download/curl-7.72.0.tar.xz.
APKBUILD包含此文件的sha256散列,该散列是在构建的最后一步中生成的。
(2)Alpine 包
openssl-dev、nghttp2-dev、zlib-dev brotli-dev、autoconf、automake、groff、libtool、perl。
6、源代码tarball可能是通过运行./buildconf && ./configure && make && ./maketgz 7.72.0,从上游GitHub仓库curl/curl@curl-7_72_0构建的。该命令其实也有一组依赖项,但这些依赖项没有被很好地记录。
7、最后,还有实际运行上述构建过程的系统。我们找不到没有任何关于他们的软件、配置或运行时状态的信息。
假设某些开发人员的机器被入侵了。那么基于以上信息,会有哪些可能的攻击仅凭该开发人员的凭据就能单方面执行呢? (以下是可能发生攻击的假设)
- 直接上传一个恶意镜像到Docker Hub。
- 让CI/CD系统从一个非官方的Dockerfile进行构建。
- 在curl/curl-docker git repo中上传一个恶意的Dockerfile(或其他文件)。
- 上传一个恶意的https://curl.haxx.se/ca/cacert.pem。
- 在Alpine的git repo中上传一个恶意的APKBUILD。
- 将一个恶意的curl-dev Alpine包上传到Alpine存储库(不确定这是否可能)。
- 上传一个恶意的https://curl.haxx.se/download/curl-7.72.0.tar.xz。(如果在计算散列之前上传,APKBUILD的散列将不会检测到)。
- 上传一个对curl/curl git repo的恶意更改。
- 攻击与供应链相关的任何系统,就像SolarWinds的攻击一样。
SLSA打算覆盖所有这些威胁。当供应链中的所有构件都具有足够的SLSA级别时,使用者可以有足够的信心,信任大多数此类攻击都得到了缓解。
请注意,以上所有这些只是针对curl自己的供应链。其他依赖关系,即Alpine基本镜像和包,也都有它们自己类似的威胁。它们也有依赖关系,依赖关系又有其他依赖关系,以此类推。每个依赖项都有自己的SLSA级别,SLSA级别的集合描述了整个供应链的安全性程度。
3.2 逐步达到 SLSA 4
接下来,让我们看看如何使用 SLSA 框架来保护上述示例中的curlimages/curl。
1、SLSA 0:初始状态
最初Docker镜像是SLSA 0。没有出处。很难确定是谁构建了构件,以及使用了什么源和依赖项。
图中显示(可变的)定位器curlimages/curl:7.72.0指向(不可变的)构件sha256:3c3ff….。
2、 SLSA 1:出处(Provenance)
可以通过编写构建脚本和“生成Provenance”来达到 SLSA 1 。构建脚本已经通过make实现了自动化,至于Provenance则可以使用简单的工具为每个版本生成。Provenance 记录了输出构件的哈希、构建器(在本例中为本地机器)以及包含构建脚本的顶级源。
在上图中,Provenance中表明该构件 sha256:3c3ff…是从 curl/curl-docker@d6525… 构建而来的。
Tips:Provenance 更详细信息参见 https://github.com/in-toto/attestation。
3、SLSA 2和3:构建服务
为了达到 SLSA 2以及将来的 SLSA 3,还需要关注构建平台,Provenance中还应尽可能包括依赖关系。SLSA 3 还需要源和构建平台来实现额外的安全控制。
在上图中,Provenance列出了一些依赖项,例如基础镜像 ( alpine:3.11.5) 和 apk 包 (例如curl-dev)。
在 SLSA 3 中,Provenance明显比以前有更多信息。这种情况下只有技术非常娴熟的攻击者才有可能伪造它。
4、SLSA 4:封闭和两人审查
SLSA 4需要两人审核源以及封闭构建。特别是封闭性保证了依赖关系是完整的。
在上面更新后的图中可以看出,其中包括了cacert.pem依赖关系,这在之前是不存在的。
在 SLSA 4,Provenance是完整且值得信赖的,没有人可以单方面改变顶级source。
5、全景
应用上面相同的步骤递归地来锁定依赖项。每个no-source依赖项都有自己的provenance,而provenance又列出了更多的依赖项,以此类推。上面最后一张图是完整图景的一个子集,其中突出显示了到上游源仓库(curl/curl) 的路径和证书文件(cacert.pem)。
实际上,由于依赖关系的递归,这个图会很大。需要有一些方法来修剪图形以专注于最重要的组件。虽然这可以手动完成,但对于如何用可扩展、通用的、自动化的方式更好完成这一工作,目前还没有一个可靠的方法。一种想法是使用生态系统特有的启发式方法。例如,Debian包是以非常统一的方式构建和组织的,这可能允许特定于Debian的启发式方法。
Google同时也发布了PoC版SLSA 1出处生成器(provenance generator),使用户可以创建并上传provenance,帮助构件符合SLSA 1。Google表示未来也希望和主要来源、组件及组件平台合作,以推广这个框架。操作演示内容参见:
https://github.com/slsa-framework/github-actions-demo。
四、小结
Google开源安全团队的专家表示:“SLSA的目标是改善软件行业安全状况,尤其是开源软件,以抵御最紧迫的完整性威胁。通过SLSA,消费者可以对他们使用软件的安全状况做出更明智的选择。”
确实,更高的SLSA级别需要对构建平台进行更强的安全控制,这使得攻击者的入侵和维持持久性更加困难。虽然SLA 4代表了理想的最终状态,但较低的级别也提供了一定的完整性保证,能使攻击者难以长时间隐藏在被破坏的开发人员环境中。
针对供应链安全风险的解决并不可能一蹴而就,这将有很长一段路要走,因此该框架的提出,虽然距离实际落地还有一段距离,但仍是一个值得持续关注并尽可能参与的方向。
参考链接
https://github.com/slsa-framework/slsa
https://thehackernews.com/2021/06/google-releases-new-framework-to.html
版权声明
本站“技术博客”所有内容的版权持有者为绿盟科技集团股份有限公司(“绿盟科技”)。作为分享技术资讯的平台,绿盟科技期待与广大用户互动交流,并欢迎在标明出处(绿盟科技-技术博客)及网址的情形下,全文转发。
上述情形之外的任何使用形式,均需提前向绿盟科技(010-68438880-5462)申请版权授权。如擅自使用,绿盟科技保留追责权利。同时,如因擅自使用博客内容引发法律纠纷,由使用者自行承担全部法律责任,与绿盟科技无关。