一.  开源软件供应链安全的兴起

Verocode研究结果表明[1],在开源组件仓库中70.5%的代码库存在安全漏洞,而这些安全漏洞风险46.6%是由其他开源项目直接、间接引进所导致的。超过96%的产业机构在其开发实现的软件应用代码库中使用开源组件。由于开源体系安全意识的淡薄以及代码重用的盛行,这一比例将持续扩大。

开源软件供应链是指开源软件在开发和运行过程中,涉及到的所有开源软件的上游社区、源码包、二进制包、第三方组件分发市场、应用软件分发市场,以及开发者和维护者、社区、基金会等,按照依赖、组合等形成的供应关系网络。

软件供应链安全包括两部分,一是引入的开源组件包中存在漏洞,二是引入的开源组件包中存在攻击者精心制作的恶意代码。

开源组件与闭源组件有着天差地别的差异,由于大部分开源组件的作者知名度不大,开发流程不完善,使用者往往缺乏专业的代码审计技能,缺乏知名公司信誉以及购买合同作背书,并且攻击者能随时下载代码做审计发现漏洞,甚至参与到发布流程中,安全风险点大大增加。例如,2020年SolarWinds的 SUNBURST攻击事件是由于闭源的原因,攻击者需要攻击到公司内部的资源才能实施投毒,攻击代价更大,相反用户对开源软件的安全性没有闭源软件那么信任。

二. 开源软件供应链中引入的漏洞

第三方组件依赖及代码复用可能导致开源组件上游的漏洞被引入到下游的组件及应用软件中,例如从GitHub抄写代码,从PyPI、Maven拉取开源组件包,都可能引入已经披露的漏洞。

针对第三方组件依赖,2014年首次披露的HeartBleed攻击是加密程序组件OpenSSL暴露的一个漏洞,当年在最热门的启用TLS的网站中有80万个网站含该组件依赖,且有1.5%易受心脏出血漏洞的攻击[2]。

针对代码复用,2018年在WinRaR中发现的漏洞(CVE-2018-20250)可导致受害者计算机完全被攻击者控制,该漏洞存在于unacev2.dll模块中,相关代码被其他220余款软件复用,包括BandZip、好压等,上述软件均受到该漏洞的影响。

Log4j是Java生态中非常流行的日志记录第三方库,Spring是Java Web开发中非常流行的Web开发框架。攻击者利用Log4j与Spring中的远程代码执行漏洞(CVE-2021-44228),进而可以控制以及窃取宿主机器上的敏感信息。根据Google安全团队的报告,Maven生态中超过8%的第三方库都受到Log4j漏洞的影响。Maven生态用户基数庞大,有很多知名框架(Struts2、Solr、Druid、Flink、ElasticSearch、Kafka等等)使用该组件,可见其覆盖的影响面非常广泛。

三. 开源软件供应链安全攻击风险点

开源软件除了引入漏洞等不安全因素,在使用的过程中还包含人为攻击的各种风险点。和其他安全风险相似的是,攻击者的主要手段是数据泄露、获取主机的权限、拒绝服务攻击以及其他经济利益的手段,因此,直接和间接使用该开源代码的用户基数越大,开源项目对攻击者的吸引力就越大。由于开源的特性,攻击者会攻击开源流程中的各个风险点以获取收益。下面将介绍开源流程中的风险点。

3.1  

恶意开源项目发布

攻击者针对特殊用户群体创建一个新的开源项目,新的项目中往往含有攻击者针对目标受众写的恶意代码,攻击者还需要宣传项目(公众号文章、博客、甚至是邮件钓鱼攻击)以吸引受害者。

HVV期间曾出现过大量案例,GitHub出现针对某平台红队开源利用代码,实际内含针对使用者的恶意代码,从而获取到红队的信息。亦或项目内出现一些链接,经红队或蓝队访问后,便掌握到红队或蓝队的出口IP。

3.2  

与合法包混淆的包名(包名抢注)

该攻击是在包储存库中部署与源包名相仿的名称,插入恶意payload,引导、误导下游用户下载部署使用,有安全人员称之为包名抢注。由于该类攻击与正常名称的项目资源是独立的,不存在相互影响,因此攻击代价往往比较低。

该类攻击通常使用以下包名混淆手段:

  • 后缀添加项目成熟度(dev|rc)、平台兼容性(i386等);
  • 对次序重新排列(test-vision-client和client-vision-test);
  • 操作单词分隔符(setup-tools和setuptools);
  • 对字母排序(dajngo和django);
  • 发布语言内置包的名称(例如Pyhon中的subprocess);
  • 品牌劫持:误认为该包是可靠的(twilio-npm)
  • 利用语义相似包名(request和requests)

3.3  

开源平台以及管理人员的安全风险

攻击者可能是开源项目的维护者,也可能是参与项目的开发、构建或分发的第三方服务提供商的成员或者外包人员。这类人的最大共同点是对项目资源或基础设施(如服务器等数据库底层代码存储库)有一定的访问权限。Faker.js库一直由Marak在维护,项目的增长与需求的增加并没有换来对应收入的增长,没有大公司愿意资助,Marak在NPM流行开发包colors提交了一个新版本,故意加入了一个死循环,导致使用colors的node.js服务器都出现了拒绝服务Denial of Service。

平台的管理者管理不善也会对下游造成严重的风险。CodeCov是一款用于托管代码测试报告和数据的在线平台,2021年其Docker镜像中泄露的凭证使攻击者可以修改一个Bash脚本。当客户下载并执行该脚本之后,客户的凭证会被泄露,攻击者进而可以访问他们的Git仓库。攻击从当年的1月31日开始,而第一个客户发现凭证遭受泄露时已是3个月之后,这意味着被入侵的软件在长达数月的时间里正常与上下游对接。HashiCorp、Confluent等多个知名企业表示受到影响。

3.4  

篡改合法的软件包

这一类维度的攻击面最为庞大,也是收益最大的一环,可以直接影响到该包的使用人群,使用者不会明显发现任何异常、不会怀有戒心。以下将展开讨论具体攻击手段。

3.4.1  

向合法开源包中注入恶意代码

对于攻击者而言,这是最有收益的手段,这将直接影响到下游的用户,无论是使用源代码还是使用预先构建的二进制文件(因为恶意代码加载时间是在二进制文件构建分发之前)。

  • 攻击者可以扮演贡献者的角色,使用假冒的合并请求将不成熟的漏洞,变成可以利用的漏洞,或者使用IDE的弱点来隐藏恶意代码,例如通过Unicode控制字符隐藏或消除代码差异。
  • 攻击者也可以通过社会工程学、维护系统的漏洞来接管项目维护者的账户,拿到修改代码所需的特权,达到修改代码的目的。
  • 篡改项目也可以通过篡改VCS(Version Control System,版本控制系统)来实现,从而绕过项目已建立的贡献工作流。通过漏洞或配置错误来改变最高权限用户账户中相关VCS的配置。

3.4.2  

在构建包的过程中注入恶意代码

有些语言的包管理器(Maven或Java的Gradle),不是从VCS下载的开源项目源代码,是从包存储库直接下载预构建的组件。恶意代码的注入就发生在预构建的过程中。这种技术对于攻击者来说是比较困难的,传播也很有限。

攻击者的手段大多分为两种,一是运行恶意的构建任务,篡改多个项目构建任务的共享的系统资源。二是通过漏洞或配置错误,接管维护者账户从而篡改构建作业。

3.4.3  

分发开源组件包的恶意版本

预构建代码通常存储在包存储库中,因此很容易去构建一个携带恶意代码的二进制文件去其他途径分发,比如CSDN等网站,或者缓存到代理中,或者通过CDN分发。

3.4.4  

重定向资源

包独立的名称或URL是定位到开源组件包的途径,当控制了这些途径,便控制了使用者下载的资源。这时候攻击者的目标是通过破坏合法项目外部的资源来使得受害者下载恶意包。这包括中间人(MITM)攻击、DNS缓存投毒或直接在客户端篡改合法URL。

存在一种特殊的攻击手法,包管理器遵循一种(可配置的)解析策略来决定从何处下载包的某个版本,以及联系多个存储库时的优先顺序。攻击者可以使用此类解析机制及其配置进行攻击。

四. 开源软件供应链中的法律问题

若是未按照开源许可证约定使用开源组件会引发潜在的法律纠纷。

许可证开源风险:一类许可证是若使用了此开源软件,那么根据开源协议后续开发的软件必须开源。最常见的许可证违反发生在GPL(GNU通用公共许可证)的使用中,一旦商用产品的组件依赖沾染到该许可证就必须开放该产品的源代码。在过往的法律纠纷中,侵权行为的表现通常是商用产品依赖开源组件却不遵循开源协议私自以闭源的形式出售、以及开发人员违规进行代码复用。FSF曾状告思科公司以Linksys品牌出售的各种产品违反了FSF拥有版权的程序许可条款,包括GCC、GNU Binutils 和GNU C库。另一案例发生在近期,起因是在谷歌供职的Joshua Bloch直接从Oracle OpenJDK复制了9行代码到谷歌的Android项目中,而Android项目没有按GPL兼容的方式授权,此行为侵犯了Oracle的著作权,为此Oracle向谷歌索赔90亿美元。

许可证修改风险[3]:React是前端开发中常用的开发框架,虽然开源但其背后由Facebook公司主导维护。2017年9月,在开发社区舆论压力下,Facebook宣布React框架将由MIT许可证授权(原为BSD+专利许可证),这一宣布将影响包括React、Jest、Flow、Immutable、Fresco、React Native、Metro、Yoga等一众开源软件。Facebook做出此决定也是属于舆论压力之下的无奈之举。原有的是BSD许可证+专利授权,意味着基于此开发的软件项目在初期虽然可以正常使用。但是假如Facebook打算发布竞品,并侵犯了你的软件专利,由于BSD+专利授权许可,那么使用者将不能借此对Facebook发起侵犯软件专利诉讼。

五. 开源软件供应链安全防护措施

目前这两年针对软件供应链的产品逐渐完善,形成针对各个流程的安全防护手段,下面主要概述针对开源软件供应链攻击的防护措施。

5.1  

最主要的措施

软件材料清单(Software Bill of Materials, SBOM)必须由项目维护者生成、维护和详细提供,理想情况下使用自动化软件成分分析(Software Composition Analysis,SCA)工具,检查所使用的组件,以及检查是否含有恶意代码或者漏洞组件。SBOM必须由包存储库安全地托管和分发,并由下游用户仔细检查他们的安全性、质量和许可证需求。

5.2  

安全身份验证

建议服务提供者提供多因素身份验证(MFA)或实施强密码策略,而项目维护人员应该遵循身份验证标准,例如,在可用的情况下使用MFA,避免密码重用,或保护敏感令牌。

5.3  

对于项目维护者

维护人员应该进行仔细审查合并请求,或者为敏感的项目分支启用分支保护规则,以避免恶意代码贡献。由于项目构建仍然可能发生在维护人员的工作站上,建议他们使用专用的构建服务。可以隔离构建步骤,这样攻击者就不能篡改构建完成的项目。

5.4  

构建包应由使用者或参与者独立完成

包的预购建工作不应该有下载时临时构建,如果由包存储库实现,这将减少破坏项目构建的风险。如果由消费者实现,这将消除与第三方构建服务和包存储库妥协相关的所有风险。

5.5  

使用代码采取安全措施

在使用时可以通过隔离代码和/或沙箱来减少恶意代码执行的影响。使用相关检测工具对代码进行漏洞、恶意代码检测。

六. 总结与思考

列出的安全风险点有助于提高对开源软件供应链威胁的认识,有助于确定给定利益相关者对供应链攻击的暴露程度。下面是我的总结内容:

1. 软件供应链安全的防护需要所有环节的人员去共同维护,攻击者需要找到链条中单一弱点即可,而防御者需要覆盖整个攻击面。

2. 应提升所有环节人员的安全意识,从根本上解决安全问题。

3. 使用规范的软件开发环节,使得代码开发流程无懈可击。

4. 完善安全审计工具,协助开发者解决自写代码、引入代码中的安全问题。

5. 对所有引入代码、开发人员不可信,对依赖的开源代码、开发代码多次扫描,防止投毒、漏洞的引入,甚至是内部人员的恶意攻击。

6. 使用者应从官网下载代码,并根据自己需求,审查许可信息。