程序语言Go以5招式防御软件供应链攻击

近期软件供应链攻击频传,最近发生在Npm上的攻击,更是由组件作者本人发动,但由于现代软件工程创建在协作之上,并且重度依赖开源软件,这使得企业软件不得不面对,来自相依项目所进行的供应链攻击。Go官方在自家博客,详细说明Go开发工具和流程设计,足以降低各阶段遭到攻击的风险。

Go防御供应链攻击第一招便是锁定构建,使得外部的更改,都无法自动影响Go的构建,官方提到,与大多数的组件文件管理不同,Go模块没有独立的限制列表,或是可以锁定特定版本的置顶文件,参与Go构建的每个相依项目的版本,完全由主模块的go.mod文件控制。

从Go 1.16开始,系统默认在go.mod不完整的情况下,构建命令包括go build、go test、go install和go run等命令将会执行失败,唯一会改变go.mod与构建的命令,是go get和go mod tidy,但这两个命令并不会自动执行或是在CI中执行,因此要对相依关系树做出更改,必须是刻意为之,并且有机会经过程序代码审查。

而且利用go get命令添加相依项目时,会使用go.mod中指定的版本而非最新版本。由于此机制,当模块被入侵并发布新的恶意版本时,在项目明确更新相依项目之前,既有程序代码都不会受到影响,而这也提供生态系统审查更改和侦测事件的机会。

另一个确保第三方无法影响构建的关键属性,是模块版本内容无法改变,因为当破坏相依项目的攻击者,可重新上传现有版本,便能够进一步破坏相依于该模块的项目。而go.sum文件包含了相依项目的加密散列列表,因此能够验证模块的真伪,仅有go get和go mod tidy能够修改该文件,而任何go.sum的任何变更都会是刻意进行的相依项目变更。

另外,Go把版本控制系统当作唯一的事实来源,官方解释,大多数的项目都是利用版本控制系统进行开发,并且上传到组件存储库,而这代表其中有两个环节可能受到攻击,其一是版本控制系统主机,另一个则是组件存储库,而后者因为较少被关注,因此更容易被上传包含恶意程序代码的组件版本到存储库。

但是在Go中,不存在组件存储库账户,组件的导入路径拥有go.mod截取组件必要的资讯,Go会从版本控制系统直接取得组件。虽然Go模块有镜像站点,但仅是一个代理,模块作者不需要注册账户,也不需要将版本上传到代理,代理使用与Go工具相同的逻辑缓存组件版本。

Go第四招防御供应链攻击,是将截取和构建的程序代码视为不受信任和存在恶意的,因此在构建程序代码后不自动执行该程序,这是Go工具链中明确的安全设计原则,和大多数生态系统在截取组件时执行程序代码不同。

官方提到,安装后的Hooks过去曾被用作感染开发者机器的手段,而这是通过模块作者传播蠕虫最方便的方法,不过,因为通常开发者在截取程序代码后,便会在之后执行,因此无论是作为开发人员机器上的测试,还是生产二进制文件的一部分,缺少安装后Hooks,也只会影响攻击者的速度,但却是一种有意义的风险缓解措施。

第五招供应链攻击防御招式,是拒绝大型相依树的文化,Go倾向复制而不是添加新的相依项目,官方提到,这可能是Go生态系统中最重要,但是技术性最低的软件供应链风险缓解措施。简单来说,在开发者使用一个函数库时,不会发现需要同时依赖其他数十个模块,而这能大幅降低供应链风险。