npm安全:防止供应链攻击
近年来,npm安全一直是媒体的一个热门话题。安全问题主要指的是那些共享的npm package
,而不是npm registry
本身。
在academic research published in June 7th 2019提出一个有意思的结论。
安装一个普通的 npm 包会引入79个第三方包和39个相关包的维护人员的隐式信任,从而潜在存在一个惊人的受攻击风险。
所以对于开发者来说,我们开发过程中需要了解供应链攻击、以及如何预防。
什么是供应链攻击?
供应链攻击是一种传播间谍软件的方式,一般通过产品软件官网或软件包存储库进行传播。通常来说,黑客会瞄准部署知名软件官网的服务器,篡改服务器上供普通用户下载的软件源代码,将间谍软件传播给前往官网下载软件的用户。[1] 此外,黑客还会向一些软件开发者常用的软件包存储库如npm、PyPI和RubyGems等注入带有恶意代码的软件包。这些软件包在用户下载后安装时会触发恶意行为[2][3]。
比较知名的供应链攻击事有XcodeGhost风波、Target公司的安全漏洞、东欧的ATM恶意软件,以及震网(Stuxnet)电脑蠕虫等。
供应链管理专家建议,为了避免网络犯罪的潜在损失,要对组织的供应网络进行严格的控管[4]。
下图是SLSA在《供应链安全白皮书》中可视化的展示了构建软件过程中的一些接点。
npm安全:如何防止供应链攻击
Prevent NPM lockfile injection
在inherent security risks with package lockfiles一文中,就阐述了npm生态系统中lockfiles
存在安全风险。npm
、yarn
赫然在列。
安全威胁发生在恶意参与者通过拉请求(pull request
)等机制获得访问权限和贡献源代码更改的能力之时。在这种情况下,如果他们要对lockfile
进行更新,比如package-lock.json
or yarn.lock
。通过引入一个新的 npm 软件包依赖项,或者甚至只是修改一个现有软件包的源 URL,那么任何软件包安装的调用(比如 npm install
或yarn install
)都会获取到上述恶意软件。
下面的屏幕截图显示了我如何通过在 npm 上对开源包发出的一个 pull 请求来注入一个恶意包。仔细看看,看看你能不能发现它:
我们CR
这个package.json
文件,大多数人肯定觉得没有什么问题。
- 我们用的都是知名的
npm package
。 - 升级的
npm package version
看起来也是合法的。
然而危险通常是在lockfile
文件,比如yarn.lock
。lock
文件的可维护性很差,肉眼很难发现一些问题。比如下面这个注入的恶意更新:
此外,JavaScript 包管理器允许用户从非常特殊的来源安装包,比如 GitHub 的 gist,或者直接从原始码储存库安装。
这意味着我可以简单地更新锁文件来指定一个新的源位置(例如在已解析的密钥中) ,作为攻击者,我可以完全控制这个位置。我还可以相应地设置 SHA512完整性值,这样就不会抛出异常。
如果这个MR请求被合并,那么该项目的使用者在yarn install
将获得我的恶意软件包。但实际上,您甚至不必等待开发人员对PR进行CR,因为许多成熟的开源项目都有一个持续集成(CI)环境。很有可能,只要创建这个 PR,一个运行在 GitHub Actions
(或者 CircleCI)上的自动构建就会开始。当它构建这个 pull 请求时,它将启动 npm 安装过程,我可以从中访问和窃取敏感信息,比如您的 CI 环境机密、 API 密钥等。
要防止这种类型的 lockfile 注入攻击,可以使用 lockfile-lint.。使用 lockfile-lint,开发人员可以对他们的 lockfile 进行 lint,并确保这些机器生成的固定依赖项的快照支持特定的信任策略。
例如,你可以告诉lockfile-lint
,如果在lint过程中发现lockfile
用了非官方的镜像文件,就直接报错失败。
所以总结下:概述针对这一潜在安全风险的预防措施:
- 在开发环境和 CI 中使用 lockfile-lint。
- 不允许外部贡献者直接更新
lockfile
。 - 使用自动依赖性升级机器人,它们以自动和可信的方式为您更新锁文件(它们从官方注册中心获取)。提示: Snyk 机器人会这么做。
Prevent arbitrary command execution
执行npm install
命令会导致你安装的软件包在安装过程中执行任意命令。
比如你在某个特定时间点执行这个命令:
npm install @vue/cli
那么你可能变为node-ipc npm package sabotage的受害者。简单说就是node-ipc
包维护者在某个版本中改用了一个有害并且有很大风险的的脚本来在您的桌面文件夹创建文件。有兴趣的可以自己看下原文。
那我们如何预防这类风险:
-
在执行
npm install
时候添加--ignore-scripts
,在安装依赖包时,确保添加–ignore-scripts后缀以禁止npm里第三方依赖包的预先安装脚本或者安装后脚本被执行;这样就可以避免一个恶意包里的病毒。但是问题来了,有些依赖包就是需要这些预先安装脚本或则安装后脚本来配置环境;如果如果我们在下载的时候,命令里用了–ignore-scripts后缀,那么是可以减轻恶意代码的危害,但同时也会导致下载的依赖包没有正常发挥作用。
那么我们如何知道什么时候可以使用–ignore-scripts后缀享受它的好处呢?就是说我们怎么样可以预先了解哪些依赖包需要脚本文件,我们不能使用–ignore-scripts后缀呢?
我们可以预先先去下载can-i-ignore-scripts 这个依赖包: can-i-ignore-scripts工具,可以帮助我们分析各个依赖包是否可以使用--ignore-scripts命令。该工具可以帮助我们确定哪些依赖包可以使用--ignore-scripts命令;实际上呢,一些我们已经运行了几个月的脚本是没啥问题的,而该工具可以帮助我们确定新加入的依赖包是否可以加入到没问题列表中。
-
考虑把
--ignore-scripts
添加到.npmrc
,这样项目协作者或者CI可以一并进行限制保护。
Avoid blind NPM package upgrades
作为持续集成(CI)过程的一部分,一些开发人员可能会显式地将所有依赖项升级到最新版本。他们这样做,同时运行测试和快乐路径测试用例,目的是确保他们的应用程序仍然按预期工作。
显然手动显示更新npm依赖项不是很好的方法。
npm update
或者
npx npm-check-updates -u
看似我们把依赖项升级到最新版本存在合法理由,但是殊不知盲目的更新版本可能存在潜在的安全风险。比如dependency confusion attacks, 以及之前发生的一些安全事件如the colors security incident, or the node-ipc security incident。
那么我们应该怎么预防,并且保持依赖不断更新。
- 使用一些智能化、自动化的一些依赖升级工具,它们一般会内置自动更新、以及依赖检查之类。比如synk。
- 仔细检查变更日志和发布构件。确保维护人员没有改变,或者有充分的理由这样做。
Prevent dependency confusion
依赖性混淆是一种攻击类型,在这种攻击中,包被创建并在组织内部使用,在某些私有代理或托管服务中,但是所述包的名称仍然可以在 npm 注册中心注册。
不幸的是,本地工具很容易配置错误。再加上 npm 包管理器获取包的方式,就会产生以下情况: 当公共 npm 注册中心存在一个同名的包,并且存在一个比已安装的版本更高的版本时,npm 将安装它。
如果微软员工不小心,或者他们的 npm 软件包管理器和内部代理服务器配置错误,以下情况会导致依赖性混淆攻击,可能渗透到公司内部基础设施。
为了检测和防止依赖性混淆攻击,您可以使用 Snyk 的免费开源工具 snync。下面是它的执行示例:
npx snync --directory ~/my-app --private “superlaser” Testing project at ~/my-app Reviewing your dependencies... Checking dependency: death-star-hyper-reactor -> ⚠️ vulnerable Checking dependency: superlaser -> ❌ suspicious
NPM security: Proactive protection from malware
您很可能已经运行了 npm install 命令来安装 npm 软件包,结果只得到如下输出:
$ npm install amp-html added 1166 packages from 1172 contributors and audited 39128 packages in 112.505s found 93911 high severity vulnerability
上面的问题是,虽然在软件包中发现了安全漏洞,但是只有在安装了这些漏洞的软件包之后才能意识到这些漏洞。那么我们是否可以在安装之前做一些安全方面的检查与探索呢?这样做可以提前避免安装到恶意软件。
这里介绍一个npq的开源项目,使用 npq,您可以根据项目是否具有 README、许可证遵从性或与其相关的 GitHub 存储库等信号进行分析,来决定是否采用。
Npq 工具甚至与 Snyk 完全集成ーー因此,如果您注册了一个免费帐户,它将获取您的环境中可用的 Snyk API 令牌,并查询 Snyk 漏洞数据库。
在我的开发环境中,我将 npm 命令别名为 npq,如下所示:
alias npm=npq
现在,每次我运行 npm install 时,它实际上都会调用 npq 来首先运行它的检查和验证。如果我选择继续,那么 npq 将返回到 npm 包管理器 CLI 以继续安装:
您还可以安装 npq 并将其作为一个特别的工具来测试漏洞:
npq install amp-html
另一种评估 npm 软件包依赖性健康状况的方法是使用 Snyk Advisor,它是搜索 npm 软件包、替代软件包或类似 npm 软件包的方便工具,可以深入了解它们的维护和安全状态。
下面是 amp-html npm 包在 Snyk Advisor 中的显示方式:
木马源攻击
木马源代码攻击是最近发表的一篇安全研究文章的名称,该文章调查了关于源代码中隐形的漏洞的问题。
下面是一段来自后端 Node.js 项目的代码片段,主要逻辑是对页面路由进行处理。它实际上包括一个木马源攻击。你能发现这里的问题吗:
module.exports = fastify => (req, res, next, access = {}) => { if (!req.query.token && access.login) { // Redirect to login return fastify.login.view(req, res); } // Debug login for issue #1812 if (req.session.user != "user // Check if admin ") { console.log("You are an admin."); } if (!req.query.token) { // Private access without token. return res.code(401).send(new Error('Missing token.')); } …
我会在我的 IDE 中打开 JavaScript 语法高亮,让你更容易使用它,所以这里有一个上面代码片段的截图:
换言之,处理下为
#!/usr/bin/env node const accessLevel = "user"; // Debug login for issue #1812 if (accessLevel != "user // Check if admin ") { console.log("You are an admin."); }
如果你执行这段代码会得到结果:
You are an admin.
其根本原因是源码中存在control characters
。那开发者怎么发现这种错误呢?
这里作者又打了一波硬广Snyk Code will also report on trojan source related issues in code:
另一个帮助检测和减轻使用 ESLint 的 JavaScript 代码库中的木马源攻击的开源工具是 ESLint 插件,ESLint-plugin-anti-Trojan-Source。
推荐阅读
- Preventing malicious packages and supply chain attacks with Snyk
- What is a backdoor? Let’s build one with Node.js
- 10 npm security best practices
原文地址:snyk.io/blog/npm-se…
转载自:https://juejin.cn/post/7188812623096807461