2. 威胁模型分析
好,咱们进入供应链安全里最烧脑、也最核心的一环——威胁模型分析。
说实话,很多人一听到「威胁建模」就头大,觉得是安全专家才干的活。但我告诉你,这玩意儿说白了就是「先想好坏人会怎么搞你,然后提前堵住路」。在供应链场景下,这个思路尤其重要。因为你面对的不仅仅是自己的代码,还有上游的依赖、下游的集成方,甚至是你根本没见过面的开源维护者。
STRIDE模型:给供应链威胁分个类
STRIDE 是微软提出的一套威胁分类框架。我最早接触它是在做 Azure 的供应链安全评估时,当时团队里争论一个漏洞到底算「篡改」还是「信息泄露」,吵了半天。后来我们用 STRIDE 一框,立马清楚了。
STRIDE 六个字母分别代表:
| 字母 | 威胁类型 | 供应链场景举例 |
|---|---|---|
| S | 欺骗 (Spoofing) | 攻击者伪造了合法的包管理器签名,让你下载了恶意包 |
| T | 篡改 (Tampering) | 有人在 CI/CD 流水线里偷偷改了你的构建脚本 |
| R | 抵赖 (Repudiation) | 上游维护者删除了某个有漏洞的版本,但你没留审计日志 |
| I | 信息泄露 (Information Disclosure) | 你的私有 npm 包被意外发布到了公共仓库 |
| D | 拒绝服务 (Denial of Service) | 某个核心依赖的仓库被 DDoS,导致你的构建全部失败 |
| E | 权限提升 (Elevation of Privilege) | 一个低权限的 CI 任务通过供应链漏洞拿到了生产环境的密钥 |
嗯,这里要注意。STRIDE 不是让你死记硬背的。我个人的习惯是,每拿到一个供应链组件,先问自己六个问题:
- 这个组件会不会被冒充?
- 它的内容会不会被改?
- 出了问题我能查到是谁干的吗?
- 它会不会把我的秘密泄露出去?
- 它会不会让我的系统变慢甚至挂掉?
- 它会不会让我拿到不该拿的权限?
你想想看,这六个问题一问完,大部分威胁就浮出水面了。
实战小技巧: 我在项目中遇到过一种情况——一个看似无害的日志库,居然在 debug 模式下会打印环境变量。这就是典型的「信息泄露」+「权限提升」组合拳。用 STRIDE 一拆,立马知道该优先修哪个。
攻击树分析:把威胁画成一棵树
STRIDE 帮你找到了「有什么威胁」,但没告诉你「攻击者具体怎么干」。这时候就需要攻击树分析(Attack Tree Analysis)了。
攻击树说白了就是一棵倒着长的树。根节点是攻击者的最终目标,子节点是达成目标需要做的每一步。我当年在帮一家金融科技公司做供应链安全评估时,画了一棵攻击树,把对方的安全负责人看愣了——他说「原来我们以为的铜墙铁壁,在攻击者眼里全是窗户」。
举个例子,攻击者的目标是「往你的生产环境植入恶意代码」。攻击树可能是这样的:
目标:往生产环境植入恶意代码
├── 1.0 通过依赖投毒
│ ├── 1.1 找到你使用的开源库
│ │ ├── 1.1.1 扫描你的 package.json
│ │ └── 1.1.2 从公开的 SBOM 中获取信息
│ ├── 1.2 上传恶意版本到包管理器
│ │ ├── 1.2.1 注册相似名称的包(Typosquatting)
│ │ └── 1.2.2 劫持维护者账号
│ └── 1.3 让你的 CI 自动拉取恶意版本
│ ├── 1.3.1 不锁定版本号(使用 ^ 或 ~)
│ └── 1.3.2 篡改 lock 文件
├── 2.0 通过 CI/CD 流水线
│ ├── 2.1 获取 CI 的访问令牌
│ │ ├── 2.1.1 从泄露的 GitHub 仓库中提取
│ │ └── 2.1.2 通过钓鱼邮件获取
│ └── 2.2 修改构建脚本
│ └── 2.2.1 在 Dockerfile 中插入后门
└── 3.0 通过内部人员
├── 3.1 收买有权限的开发者
└── 3.2 利用离职员工的未回收账号
画完这棵树,你就能清楚地看到:哪些节点是「低 hanging fruit」(容易攻破的点),哪些节点需要组合攻击才能生效。我个人习惯用不同颜色标记:红色是高风险路径,黄色是需要多种条件才能触发的路径,绿色是理论上存在但实际很难利用的路径。
避坑指南: 我曾经犯过一个错误——只画了攻击树,但没有给每个节点赋「难度值」和「检测能力值」。结果团队花了很多精力去堵一个几乎不可能发生的攻击路径,反而忽略了那些每天都在发生的简单攻击。记住,攻击树的价值在于「优先级排序」,不是「画得好看」。
信任边界划分:你的系统不是铁板一块
最后一个概念,也是我个人认为最容易被忽视的——信任边界。
什么叫信任边界?就是「你默认信任谁,不信任谁」。在供应链场景下,这个边界比你想象的要模糊得多。
举个例子,你从 npm 下载了一个包。你信任这个包吗?你可能觉得「官方仓库的包应该没问题」。但你想过没有——这个包的维护者可能只有一个人,他的电脑可能已经被攻破了。你信任的其实是「包管理器的签名机制」,而不是那个包本身。
我建议你把供应链中的信任边界画成一张图:
信任边界示例(从内到外):
[最内层] 你的生产环境 → 只运行经过签名和扫描的代码
[第二层] 你的 CI/CD 环境 → 信任经过验证的构建产物
[第三层] 你的私有仓库 → 信任经过审核的内部包
[第四层] 公开包管理器 → 不信任任何包,必须经过扫描和签名验证
[最外层] 互联网 → 完全不信任,所有输入都是潜在的威胁
每跨越一层信任边界,就必须有一次安全检查。比如:
- 从公开包管理器下载的包,进入私有仓库前必须经过漏洞扫描和签名验证
- 从私有仓库进入 CI/CD 环境时,必须校验签名是否被篡改
- 从 CI/CD 环境进入生产环境时,必须经过完整的 SBOM 比对和合规检查
注意: 很多公司把信任边界画得太粗了。比如「我们信任 GitHub,所以从 GitHub 下载的代码都是安全的」。你想想看,GitHub 本身是安全的,但 GitHub 上的每个仓库呢?每个 Pull Request 呢?每个 Issue 里的评论呢?这些都是潜在的威胁入口。信任边界一定要画到「单个组件」级别,而不是「整个平台」级别。
好了,总结一下这一章的核心:
- STRIDE 帮你分类威胁,知道「有什么问题」
- 攻击树 帮你拆解攻击路径,知道「问题怎么发生的」
- 信任边界 帮你划定安全域,知道「在哪里设防」