近来,频繁发生的 npm 供应链攻击令开发者们精疲力竭:
流行 npm 包 faker.js 和 colors.js 的作者为抵制“开源白嫖”而自毁项目,导致数千个应用崩溃;
知名 npm 包 node-ipc 的作者以“反战”为名,向项目注入恶意代码;
攻击者针对 Azure 开发者,创建了向 200 多个恶意 npm 包以窃取 PII(个人身份信息);
代号为“RED-LILI”的黑客发动了大规模 npm 供应链攻击,一口气发布近 800 个恶意 npm 包。
这一系列接连不断的 npm 供应链攻击令许多人开始怀疑:Node.js 包究竟还值得信任吗?对于这个问题,知名开源框架 sharedb/ottypes 作者 Seph Gentle 通过一篇文章给出了他的回答:“Node.js 包已不值得信任。”
文章开头,Seph Gentle 就明确指出:“npm 的根本问题是,你安装的所有软件包都可以在计算机上执行任何操作。”
而这个“任何”包括:阅读电脑上的所有文件,例如电子邮件、密码等所有内容;编辑、删除或者给文件加密码锁;在互联网上做任何它想做的事;运行子进程、更改操作系统设置、安装关键日志记录器……
举个例子,本来你只是想简单安装一个 leftpad 模块图省事,结果它其实拥有对服务器和网页的完整访问权限,包括访问我们的个人隐私数据。
当然了,可能大多数的 Node.js 包和模块都没有恶意,但不得不说,这种仿佛敞开大门欢迎所有人来参观的设定有一定隐患。Seph Gentle 对此感慨:“坦率地说,我很惊讶供应链攻击居然没有更频繁地发生。”
不过,正如 Seph Gentle 所说,即便知道这个隐患,我们也很难先发制人地找出哪些 Node.js 包有问题,或者哪些开发人员不值得信任。
说到这里,可能会有人提到与 Node.js 一样同为 JavaScript 运行时却更为安全的 Deno:Deno 对于各种访问权(文件、网络和环境)都有严格限制。但 Seph Gentle 认为:“我认为它(Deno)还不够好。”
Deno 允许开发者在命令行中指定程序可执行哪些类型的操作、访问哪些权限等,但其不足之处在于允许范围过大了。例如,开发者正在制作一个网络服务器,难道就要允许 leftpad 可访问全部互联网?同理,即便开发者正在制作文件服务器,leftpad 也不应访问其全部文件系统。
Seph Gentle 对此总结道:“Deno 或许是一个好的开始,但它分得还不够细。”因此,Seph Gentle 还是想针对 Node.js 当前的处理方式提出可改进的地方,而此时,他注意到了 OpenBSD API 的 pledge 程序。
OpenBSD API 中 pledge 程序的工作方式是:当程序开始但还未做任何事情之前,开发者需做出一套设定以确保该程序将仅使用某些资源,例如:“我保证这个程序不会访问 /some/path 之外的任何文件,也不会连接到除 example.com 之外的其他节点。”这样的话,即便该程序被入侵,也无法实行权限之外的行为。
在此灵感启发下,Seph Gentle 认为 Node.js 的工作方式可以做出一些改变,以彻底解决现存隐患:
可添加一个名为 capabilities 的新内置 Node.js 库,用来分发功能令牌,且该令牌只能由 capabilities 库创建。
在进行任何特权操作时(如访问文件系统、网络、硬件、生成子进程、加载原生 npm 模块等等),调用者需在相关函数中添加相符的功能令牌。
每个功能令牌都要设定一个范围,指定该令牌的持有者可以做什么,且后续 capabilities 库也只能在此基础上缩小功能,不能扩大。
当程序启动时,只有你的主模块可以拥有“可以做任何事”的功能。之后,你可以缩小并将此功能令牌传递给其他包,具体缩小范围取决于你希望它们做什么。
以上四步如能完美落实,相信 npm 供应链攻击将大幅减少,现阶段“Node.js 包是否值得信任”的问题或许也就可以解决。
但 Seph Gentle 坦言,如果将目光聚焦到具体实施细节,会发现有很多棘手问题还需优化:
1、开发者应如何准确表达功能令牌的权限范围?
2、要如何安全地将“通配符”令牌传递给主模块?
3、是否存在一些 JavaScript 技巧可让攻击者轻松避开整个系统?又有什么方法可以让我们在严格功能模式下,锁定更大范围的 JavaScript?
截止目前,Seph Gentle 还找出这些问题的答案,因此他呼吁道:“这是一个值得接受的挑战,毕竟它关乎计算机和用户数据的安全。JavaScript 生态系统中有很多聪明人,希望你们能站出来接受这个挑战。”
Seph Gentle 的这篇文章在 HN 上引起了巨大讨论,Deno、Socket(一个由 npm 维护者构建的新工具,用于帮助解决 JavaScript 供应链安全问题)等项目核心开发者也纷纷留言。
Deno 核心团队成员:
“我们过去也曾考虑过 Deno 基于功能限制的安全性,但我们的结论是——在不默认冻结所有原型和对象的情况下,这是不可能在 JavaScript 中安全实现的,因为你需要确保功能令牌永远不会泄漏。
由于 JavaScript 的动态特性,攻击者可以窃取令牌。而仅仅冻结所有内在原型和对象也是不够的,因为他们总会找到转移代币的方法。”
Socket 创始人:
“我完全同意我们应该假设所有开源软件包都可能是恶意的想法。Socket.dev 使用“深度包检查”来描述开源包的行为。通过实际分析包代码,Socket 可以检测包何时使用与安全相关的平台功能,如网络、文件系统或 shell。
通过这种方式,Socket 可以检测供应链攻击的迹象,包括引入安装脚本、混淆代码、高熵字符串或使用特权 API。总之,我们正在采用一种全新的方法来解决行业的安全领域中最困难的问题之一,且这个行业一直痴迷于报告已知的漏洞。”
许多网友对此也看法不一:
@chha:“至少在目前的情况下,的确没有任何软件包或存储库值得信任。文章所提出的方法只能解决问题的一半,我们还需要一种能使包接受审查的方法,其中发布者和存储库的包签名必须是强制性的。”
@didip:“在 Node.js 诞生之初,npm 公司有一个营销术语:包越小,可重用性越好。可现在,它似乎永远玷污了 Node.js 生态系统,这也就是为什么我对 Deno 更为期待。”
那么,对于 Seph Gentle 提出的解决方案,你有什么看法吗?
参考链接:
https://josephg.com/blog/node-sandbox/
https://news.ycombinator.com/item?id=30988034
END
《新程序员001-004》全面上市,对话世界级大师,报道中国IT行业创新创造
成就一亿技术人