原文作者:G Ferreira, L Jia, J Sunshine

原文标题:Containing malicious package updates in npm with a lightweight permission system

原文链接:https://ieeexplore.ieee.org/abstract/document/9402108

原文来源:ICSE 2021

笔记作者:outx@SecQuan

文章小编:cherry@SecQuan

0x01 Intro

本文主要涉及软件供应链安全,尤其涉及到可重用第三方组件安全。作者聚焦于Node.js生态,重点关注软件更新过程中的组件包安全,注:这里主要是由于Node.js鼓励自动更新这一特性所导致的独有安全风险。相较于现有的供应链防御手段在实际部署至实际软件工程环境中容易出现的问题(包括,代价昂贵,架构重构,仅能预防已知风险等),作者从大部分正常组件包仅需要简单的一些操作这一特点出发(复杂操作包括,访问文件系统/网络API等),实现了一个实用的轻量级解决方案。

总的来说,作者主要:

  1. 设计了一个轻量级的权限系统,可以保护Node.js程序免受大量恶意组件包的更新;

  2. 讨论了设计上的权衡,以强调在大规模检测策略下低成本的方案的好处;

  3. 在大量的组件包和应用程序上评估了该解决方案;

  4. 提出了实施和评估的基准,https://github.com/gabrielcsf/malicious-updates-icse2021。

0x02 Threats

NPM的安装过程可以简述为,从NPM存储库中下载组件包->安装和更新包->递归安装相应依赖(本质上也是组件包)。当然,最大的问题是NPM包管理器本身的设计是鼓励自动更新的。具体来说,这些特性对应着以下风险:

  • Javascript和Node.js仅提供了一小部分内置模块,大部分的都靠社区,这无形中扩大了攻击面

  • Node.js社区更中意小的组件包(受Unix理念的影响),所以在一个Node.js项目中大量第三方的组件包是很常见的

  • 开发人员通常会根据实际情况选择一定范围内均可用的组件包,以便于及时安装更新,使用一些更加便利的功能

  • 向NPM社区提交组件包仅需要单个命令,而并不需要进一步的内容安全检查或是代码质量检查

  • 大多数组件包也同样会有着自己的依赖组件包,而这些所谓的间接依赖组件包对于开发人员来说是不可见的,这一点格外吸引攻击者,谁又不想悄悄地在一个特别流行的组件包中添加上一个恶意的依赖,以便于实现大规模供应链攻击呢

  • NPM安装过程中通常是一条命令执行完毕,其安装过程中所有会执行的命令都会使用这一权限,这是会有安全隐患的

0x03 Permission System

作者的目的是构建一个权限系统,用于在Node.js程序中对其组件包进行沙箱处置,并严格控制其权限,换句话说,作者在组件包的层面上执行了最小权限设计思路。该设计可以为一个大的组件包子集提供防护而不需要改变其本身的结构,最重要的是这个过程的开销可以忽略不计。

权限系统的设计主要为了实现三个目标:

  1. 权限系统能够控制并消除某一些攻击来减少攻击面

  2. 权限系统不应该大幅度地改变本来的配置,同时不能够破坏现有的用户代码

  3. 权限系统的运行过程应当具有较低的性能开销

作者在本文中仅关心了一种攻击链,即组件包更新。该攻击链可以简述为:攻击者通过使用Git库中泄露的NPM包管理器凭证(亦或是弱口令ssh、钓鱼、社工等)-> 获得对包开发者机器的权限 -> 发布恶意组件包更新。

在权限集设计上,作者主要考虑四个易于理解的权限:

  • 网络连接权限,具体来说,内置模块中http/http2/https/net需要这个权限。关闭该权限后,恶意组件包将无法通过网络窃取用户数据

  • 文件系统权限,具体来说,内置模块中fs需要这个权限。关闭该权限后,恶意组件包将无法进行读取、写入或删除本地文件

  • 进程创建权限。具体来说,内置模块child_process需要这个权限。关闭后,恶意组件包将无法尝试反弹shell或是清理进程

  • 元编程权限,要理解这一概念,可以参考eval函数。关闭后,恶意组件包将无法影响全局环境(如本地对象的原型)。这个权限尤其需要注意,因为它可以用于规避其他三个权限的限制。

而这个所谓的特定设计是指,对于每个组件包来说,该组件包中的代码只能从具有相同或更少权限的组件包中导入模块。换句话说,组件包的权限是以传递方式进行组合的,要依赖另一个组件包,那么该包至少需要具有相同的权限。这对于开发人员来说是十分具有意义的,因为他们仅需要限制住他们需要导入的组件包的权限,便能轻松控制其后续间接依赖组件包的权限,而不用一个一个梳理和排除。所以其实从本质上来说,作者是在一个轻量级的沙箱策略的基础上结合静态分析进行检查。

0x04 Evaluation

作者这这项研究主要回答了4个问题:

  • RQ1: 权限系统能够有效地处置NPM存储库中多少个包?

  • RQ2: 权限系统在遏制eslint-scope、event-stream等攻击面的效果怎样?

  • RQ3: 权限系统开销如何,是否透明?

  • RQ4: 在现实情况下,权限系统能够为组件包更新节省多少审查工作量?

Answer to RQ1

权限系统能够处置14-33%的组件包,这是因为这部分组件包并不需要任何权限;同时也还包括仅需要一个权限的1-11%的组件包。除此之外的组件包将无法获取以上跟安全相关的资源,这的确减少了攻击面。

Answer to RQ2

由于那些不存在恶意更新的组件包在其被攻击者注入有恶意代码之前是不需要任何其他权限的,在对过去出现的一些恶意组件包进行检查时,权限系统能够有效地遏止这类攻击。

Answer to RQ3

权限系统限制仅会限制直接依赖的组件包而不是那些迭代的间接依赖,因此效率很高。而且如果正确地针对组件包设置好权限,这对于该更新过程来说是完全透明的。

Answer to RQ4

NPM组件包更新迭代频繁,因此作者这一方案能够大大减少更新的审查工作量。

0x05 Conclusion

作者为了应对组件包更新中出现的安全风险,提出了一个权限系统,即将更新的组件包置于沙箱中处置而非整个应用程序,确保那些恶意更新无法动用一些敏感权限。

读者在读完整篇文章后主要有一个问题,限制权限后进行更新的确可以做到保护Node.js程序,但问题是一旦直接依赖组件包的权限没有做好,那岂不是没法更新,要将其部署至实际软件工程环境中,作者还需要在安全和用户体验上的权衡上再考究考究。

声明:本文来自安全学术圈,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。