前言
2020年12月,全球最大的软件供应链攻击震惊了整个安全行业。
攻击者入侵了 SolarWinds 的 CI/CD 管道,在官方签名的 Orion 更新包中植入了 Sunburst 后门程序。这个更新包经过了合法的数字签名,通过了 SolarWinds 官方的发布流程,推送到了约 18,000 家企业和政府机构。FireEye(现Mandiant)、Microsoft、VMware、Cisco 等顶级科技公司纷纷中招,美国国土安全部、财政部、商务部全面沦陷。
这一切发生的前提只有一个:软件供应链的完整性验证机制存在缺陷。
这是 OWASP Top 10 2021 中 A08: Software and Data Integrity Failures(软件和数据完整性失败)的典型案例。此类别在 2017 版中并未独立出现,它是将不安全的反序列化、CI/CD 供应链问题、不安全的自动更新等零散漏洞整合为一个统一的攻击面——攻击者不再需要正面攻破你的系统,只需要攻破你信任的源头。
一、为什么软件和数据完整性是安全问题
1.1 信任链与完整性验证的本质
现代软件系统的运行,建立在一系列隐式信任假设之上:
用户程序
↓ 依赖
第三方库(Maven/ npm / PyPI)
↓ 依赖
基础镜像(Docker Hub / 公共仓库)
↓ 来源
自动更新服务
↓ 来源
CI/CD 管道(代码提交 → 构建 → 测试 → 发布)
每一层都默认上游提供的内容是可信的、完整的、未被篡改的。一旦某个环节的完整性验证缺失或不足,攻击者就可以从最薄弱的信任节点发起攻击,而下游所有依赖方全部沦陷。
1.2 完整性保护的三要素
| 真实性(Authenticity) | ||
| 完整性(Integrity) | ||
| 可用性(Availability) |
1.3 从 SolarWinds 看软件供应链攻击链
攻击链分析:
Phase1: CI/CD 入侵
攻击者通过密码喷洒(Password Spraying)获得 SolarWinds 内部 VPN 凭证
访问内部 Exchange 服务器,发现 Orion 平台构建系统权限配置不当
↓
Phase 2: 代码注入
在 Orion 源代码中植入 Sunburst 后门(隐藏在合法的类/函数中)
构建系统使用被污染的源码打包成更新包
↓
Phase 3: 签名绕过
SolarWinds 的代码签名流程存在缺陷:
签名发生在构建之后,但构建过程本身未被监控
攻击者的代码在签名后才被插入(build re-injection)
↓
Phase 4: 恶意更新分发
受污染的更新包通过官方渠道推送,附有合法数字签名
自动更新机制无完整性强校验,直接安装
↓
Phase 5: 横向移动(后渗透)
Sunburst 后门建立 C2 通信,窃取凭证,横向移动
部分目标被进一步部署了 TEARDROP / RAINDROP 等二次攻击工具
为什么传统安全设备无法检测:
- 更新包有合法签名 → SSL/TLS 加密传输 → 内容加密 → WAF/IDS 看不到
- 后门代码与合法代码混淆 → 静态扫描无法识别
- C2 通信伪装成正常 DNS 查询 → 防火墙放行
二、软件和数据完整性失败的分类
2.1 不安全的反序列化(Insecure Deserialization)
原理:
序列化是将对象转换为字节流的过程(便于存储或传输),反序列化是将字节流还原为对象的过程。许多编程语言内置序列化机制,开发者经常反序列化来自不可信来源的数据。
风险所在:
反序列化不仅仅是"还原数据",在某些语言中,它会触发对象构造、getter/setter 等副作用。如果反序列化过程中触发了危险方法(如 Java 的 readObject、Python 的 __reduce__),攻击者可以通过构造特定的序列化 payload,在反序列化时执行任意代码。
Python pickle 反序列化示意(原理层面):
# pickle.loads() 从不可信数据恢复对象
# 如果对象定义了 __reduce__,反序列化时会自动调用
import pickle
classMalicious:
def__reduce__(self):
# 反序列化时执行:os.system('id > /tmp/pwned')
import os
return (os.system, ('id > /tmp/pwned',))
# 攻击者序列化这个对象
malicious_data = pickle.dumps(Malicious())
# 受害者反序列化,代码即执行
pickle.loads(malicious_data) # RCE!
OWASP 分类依据:
反序列化漏洞的危害极大(通常直接 RCE),但利用条件相对复杂(需要目标使用特定序列化库+接受不可信输入)。在 2017 版 OWASP 中曾与注入合并,在 2021 版中部分归入 A08。
2.2 不安全的 CI/CD 管道(Insecure CI/CD Pipeline)
攻击面分析:
CI/CD 管道是现代软件交付的核心,通常包括:代码提交 → 静态扫描 → 构建 → 单元测试 → 集成测试 → 制品签名 → 镜像推送 → 部署。
每个环节都是潜在的攻击面:
| 代码提交 | ||
| 依赖引入 | ||
| 构建系统 | ||
| 镜像仓库 | ||
| 签名流程 | ||
| 部署配置 |
CI/CD 管道攻击的独特危害:
- 放大效应
:一次成功的 CI/CD 攻击,可以自动扩散到所有使用该产品的客户 - 信任滥用
:恶意内容通过了所有安全检查流程(因为安全检查本身也被攻击者利用) - 持久化
:后门被嵌入在定期更新的软件中,可以长期潜伏
2.3 不安全的自动更新机制(Insecure Auto-Update)
原理:
软件应用的自动更新功能,通常遵循以下流程:
检查更新服务器 → 下载更新包 → 验证版本号 → 安装
如果完整性验证缺失或不足:
真实案例: Notepad++ 供应链事件(2017):
攻击者入侵了 Notepad++ 的 SourceForge 文件分发账户,将官方安装包替换为植入木马的版本。虽然最终因数字签名不匹配被部分安全软件拦截,但事件暴露了分发环节的完整性验证缺失问题。
2.4 依赖来源不验证(Reliance on Untrusted Data Without Integrity Check)
核心问题:
应用程序经常需要:
- 从 CDN 加载 JavaScript 库
- 从外部 API 获取配置数据
- 从插件市场安装扩展
- 从配置文件读取序列化数据
如果这些外部数据没有完整性验证(数字签名/哈希校验),攻击者可以通过以下方式实施攻击:
攻击向量分类:
外部数据来源 → 攻击方式
─────────────────────────────────────────────
CDN 劫持(路由投毒/DNS 劫持)→ 替换 JS 库为恶意版本
→ 后果:XSS 窃取 Cookie、键盘记录、页面注入
API 响应无签名验证 → 中间人篡改 API 响应
→ 后果:业务逻辑被绕过、敏感数据被篡改
插件市场账户被入侵 → 发布含后门的插件更新
→ 后果:RCE 或数据窃取
序列化数据无完整性校验 → 反序列化攻击(见 2.1)
→ 后果:RCE
第三方配置文件被篡改 → 注入恶意配置改变应用行为
→ 后果:权限提升、认证绕过
特别警示:
2021 年,攻击者通过抢注 PyPI 弃用包名称(Typosquatting),发布了 requests(正确包名)的伪造版本 requesets、request、requeset 等,成功骗取开发者的 pip 安装。这些伪造包包含窃取环境变量的代码,在 CI/CD 管道中自动执行。
2.5 序列化数据的完整性失败
除了 2.1 中讨论的反序列化 RCE,更广泛的序列化数据完整性问题还包括:
Cookie / Session 篡改:
某些应用将敏感数据序列化后存储在 Cookie 或 localStorage 中,客户端可以通过修改序列化数据绕过业务逻辑(如修改 role: "user" 为 role: "admin")。
JWT 签名绕过:
JSON Web Token 如果使用了 alg: none(无签名)或使用了弱算法(如 HS256 但使用公开知道的公钥作为密钥),攻击者可以伪造任意权限的 JWT。
配置文件注入:
应用读取 YAML/JSON 配置文件时,如果配置文件来源不可信且缺乏 Schema 校验,攻击者可以通过注入恶意配置(如 SpEL 表达式、模板注入、JNDI 引用)实现代码执行。
三、真实案例深度解析
3.1 Equifax 数据泄露与 Apache Struts 反序列化(2017)
事件背景:
Equifax 是美国三大信用报告机构之一。2017年9月,宣布发现了一起影响 1.47 亿人的数据泄露事件。攻击者利用 Apache Struts 2 的 CVE-2017-5638(S2-045)远程代码执行漏洞,窃取了姓名、SSN、出生日期、地址、驾驶证号码、信用卡号等敏感数据。
漏洞原理(技术层面):
Apache Struts 2 的 Jakarta Multipart Parser 在处理 Content-Type 头时存在漏洞。攻击者可以通过构造恶意的 Content-Type 头,注入 OGNL 表达式,在服务器端执行任意代码:
Content-Type: %{(#_='multipart/form-data').(#[#context:['com.opensymphony.xwork2.dispatcher.HttpServletResponse']].addHeader('X',...)}}
OGNL 表达式在处理时被 Struts 2 的过滤器解析,攻击者可以借此执行系统命令。
数据完整性失败的维度:
- 反序列化层面
:Struts 2 使用 OGNL 表达式解析用户输入,OGNL 反序列化过程中触发代码执行 - 输入验证层面
:Content-Type 头本不应该被用作表达式求值,但 Struts 2 错误地将用户输入传递给了 OGNL 引擎 - 补丁验证层面
:Apache 在 2017 年 3 月已发布 S2-045 修复补丁,但 Equifax 直至 5 月仍未打补丁(补丁验证机制缺失)
损失:
- 约 1.47 亿用户数据泄露
- 集体诉讼赔偿 7 亿美元
- CEO、CSO 辞职
- FTC 罚款 5.75 亿美元
3.2 event-stream npm 包投毒事件(2018)
事件背景:
event-stream 是一个广泛使用的 Node.js 工具库(每周下载量超过百万)。攻击者通过 GitHub 建立了对该项目的信任后,向 npm 提交了代码捐赠,将自己的账户添加为 event-stream 的维护者之一。
攻击过程:
攻击者联系 event-stream 原作者,提出代码捐赠
↓
原作者将 npm 发布权限转让给攻击者
↓
攻击者发布 event-streamv3.3.6,在依赖中添加 flatmap-stream@0.1.1
↓
flatmap-stream 包含恶意代码:窃取比特币钱包私钥
↓
大量 Node.js 应用(包括 Copay 比特币钱包)自动安装了带毒版本
↓
Copay 用户的比特币私钥被窃取
数据完整性失败的维度:
- 供应链信任
:npm 的代码捐赠机制缺乏安全性审核,没有验证新维护者的真实身份 - 依赖完整性
:event-stream v3.3.6 的 package.json中引入了一个看似无害的flatmap-stream依赖,没有人会想到这会是一个专门窃取比特币钱包的后门 - 版本锁定缺失
:大多数项目使用 ^3.3.4这样的宽松版本范围,自动接受了 3.3.6 恶意更新
3.3 Homebrew GitHub Actions 供应链攻击(2021)
事件背景:
Homebrew 是 macOS 最流行的包管理器。2021年4月,一名攻击者利用 GitHub Actions 的自动化流程漏洞,向 Homebrew 的 Homebrew-core 仓库提交了一个恶意 GitHub Actions 工作流。
攻击方式:
攻击者向 Homebrew-core 仓库提交代码贡献(正常流程)
↓
代码贡献通过审查后,被合并到主分支
↓
GitHub Actions 自动触发 brew bump-formula-pr 工作流更新版本
↓
攻击者在工作流中注入恶意代码:提取 GitHub Actions secret 和 API 令牌
↓
以 Homebrew 官方 GitHub 机器人身份执行,权限极大
关键问题:
Homebrew 的 GitHub Actions 工作流使用了被攻击者控制的工作流触发条件(pull_request_target),使得攻击者的代码可以在 Homebrew 官方 GitHub token 的上下文中执行,从而窃取仓库权限。
四、软件和数据完整性失败的危害评估
4.1 危害等级矩阵
| 可利用性 | ||||
| 普遍性 | ||||
| 影响范围 | ||||
| 技术影响 | ||||
| 业务影响 |
4.2 为什么 A08 在 OWASP Top 10 中独立成类
整合逻辑:
2017 版 OWASP 中,不安全反序列化是 A08(注入的一部分),不安全的 CI/CD 和供应链问题没有独立分类。随着 SolarWinds、Log4Shell、event-stream 等大型供应链安全事件的频发,2021 版将这些问题整合为 A08,强调的核心观点是:
现代应用的威胁,已经从"正面攻破应用"转向"攻破应用所信任的一切"
五、防御思路概览(预告)
本期是原理与分类篇。后续两期将分别从实战利用和防御方案两个维度展开。防御方案篇将覆盖:
- CI/CD 管道安全审计与加固
- 软件签名与完整性验证最佳实践
- 依赖安全审计(SBOM + 依赖扫描)
- 自动更新安全架构设计
- 序列化安全的全面解决方案
📚 参考框架
🛡️ 理论先行,实践跟进
⚠️ 提示:本文仅供网络安全学习研究使用。所有技术方案应在获得合法授权的范围内使用。未授权的安全测试违反中国《网络安全法》及相关法规。
夜雨聆风