发生了什么,本文又补充了什么
2026 年 3 月 31 日,一个被攻陷的维护者账户发布了恶意的 axios 1.14.1 和 0.30.4 版本。维护者事后复盘确认新增的依赖为 plain-crypto-js@4.2.1,并描述了跨平台的恶意软件投递。[1] Microsoft 的分析认为执行发生在软件包安装阶段,而不是 HTTP 客户端正常源码逻辑的变更。[2]
本文提出了一套恢复证据模型:确定安装了什么、执行了什么,以及该执行能够触及哪些权限。这些问题能帮助产品负责人判断哪些系统和发布产物需要关注,而不是把每一次依赖出现都当作已确认的入侵。本分析基于 2026 年 10 月 4 日查阅的公开资料,并未声称访问过受害者的环境,也没有进行新的恶意软件实验。
安装本身就是一道执行边界,即使没有 import
JFrog 指出了一个 postinstall 钩子;执行并不需要应用层的 import。[3] 对审计的启示是:源码层面的可达性和安装阶段的可达性需要分开调查。如果审查只停留在“应用是否导入了可疑的库”,就可能漏掉机器实际暴露的那个阶段。
下面的图示是信任转移的概念模型,并不是说所有列出的下游系统都遭到了攻陷。请盘点在您的执行机上运行的安装器、包管理器设置、脚本、插件和构建工具,并把安装任务与拥有部署或签名权限的任务分开。即使是打包后不含任何 Axios 代码的移动端或桌面端产品,这种隔离同样重要。
registry publication
-> dependency resolution
-> package installation
-> lifecycle-script execution
-> runner identity and reachable credentials
-> repositories, signing systems, deployments
The package tree is one evidence source.
The execution history is a different evidence source.为什么现在干净的依赖树可以与过去的恶意执行并存
JFrog 报告称,攻击者在执行后删除了加载器并替换了其清单文件。[3] 对调查人员而言,这意味着事后的文件检查可能只是一份不完整的历史记录。这并不是说所有扫描工具都失效,而是说明现状检查和执行历史调查回答的是两个不同的问题。
在重建环境之前,请保留安装日志、锁定文件的历史版本、缓存包的哈希值、Runner 遥测数据、网络记录和发布元数据。在今天的检出代码中搜不到结果,并不能证明 Runner 昨天安装了什么。反过来,锁定文件中出现恶意版本只能证明依赖解析发生过,并不自动等于载荷成功执行或数据被窃取。请让每一项结论都停留在证据能够支撑的程度上。
用三种证据状态替代“受影响/未受影响”的二元标签
第一种状态是解析证据:恶意版本的软件包出现在安装记录、锁定文件或包产物中。第二种是执行证据:某个生命周期钩子、子进程或相关网络活动确实运行过。第三种是权限暴露:该次执行能够访问特定的凭据或具有特权的操作。这些是本文建议的分诊状态,而不是官方的事件分类;同一次调查对这三者的确信程度可能各不相同。
当日志缺失时,请保留“未知”这一取值。缺少执行遥测数据,并不能证明某个脚本从未运行过;同样,能够触及某个凭据,也不能证明攻击者确实使用了它。请记录受影响资产、时间区间、证据以及不确定性,再由事件负责人根据风险选择相应的控制措施。这样得出的决策,远比凭一次 grep 结果就宣布整个组织“安全”或“已被攻陷”更有价值。
asset_id | workflow_run | install_time_utc
package_name | package_version | package_digest
script_policy | execution_evidence | network_evidence
identity | credential_scope | credential_lifetime
artifact_digest | downstream_destination
containment | credential_revocation | rebuild_evidence恢复工作需要主机、身份和产物三方面的证据
Microsoft 建议检查受影响的安装活动,并轮换曾暴露给被攻陷系统的凭据。[2] 移除这个依赖只是纠正了软件包的状态,既不能吊销被盗的令牌,也不能证明某个 Runner 是干净的。请协调控制措施和证据保全工作,从可信环境中吊销已暴露的访问权限,并在调查确有需要时,基于可信基线重建受影响的执行环境。
请从受影响的工作流追溯到各个发布版本的产物哈希、签名事件和分发目的地,确定哪些产物需要撤回、检查或彻底重新构建。验证旧凭据确实已失效、替换凭据的权限范围符合预期,并确认新的构建不再解析恶意软件包。请保留实际安装树和已执行工作流的证据,而不是把一份已修正的 package.json 当作完整的恢复报告。
在每一次信任转移处设置相应的控制措施
提交到版本控制的锁定文件和不可变的安装模式,有助于复现依赖解析结果,但并不能让一个被钉住版本的恶意包变得可信。发布年龄策略能为调查争取时间,却不能证明较旧的包就是无害的。来源证明可以把产物与发布者和构建过程关联起来,但一个被授权、却已遭攻陷的工作流仍然可能产出不该存在的产物。请让每一项控制只承担它真正能保证的那部分,而不要让它们彼此替代。
Google 的供应链指南建议采用分层控制,例如隔离的 Runner、对生命周期脚本的限制,以及短生命周期的工作负载身份。[5] 请检查实际部署的包管理器版本的默认行为;不要假设所有版本执行或阻止脚本的方式都相同。明确允许必要的构建钩子,最大限度减少安装任务中的凭据,限制出站流量,并测试确认被拒绝的钩子确实无法执行。只有在验证过实际行为之后,一项安全设置才能算作一道真正的边界。
产品负责人在恢复阶段应当提出的问题
哪些 Runner 和开发人员的机器解析到了这个恶意包?哪些实际执行了其中的代码?当时这些环境能够访问哪些身份?哪些产物、乃至哪些客户(如果有的话)与这些运行相关?有哪些证据支撑凭据吊销和干净的重新构建?请要求逐个资产给出答案,并明确标出仍不清楚的部分,而不是接受一句没有依据的笼统保证,或一个耸人听闻的受害者数量估计。
一次范围明确的修复验证工作,可以核查已约定的依赖修复、工作流权限、身份变更和重建后的产物。对活跃事件的控制和主机取证,可能需要专门的响应团队来完成;在委托工作之前,先把这部分责任划分清楚。可以反复使用的成果,是一份证据记录——当下一个受信任的依赖再次跨越安装这道边界时,开发人员、安全团队和发布负责人都能回头查阅它。
参考来源
需要在您自己的产品中检查这个问题吗?
漏洞修复与修复验证想学会自己发现这类漏洞吗?
安全代码审计课程(一对一)