每种威胁建模方法都从同一个问题开始:我们在做什么?通常的答案是一张数据流图。它是一张只有五类元素的简单图,但在安全评审之前,花一小时画它最值得,因为大多数威胁都出现在数据从一个信任级别进入另一个信任级别的地方。
本文介绍如何为威胁建模绘制数据流图(DFD)、如何放置信任边界,以及如何用 STRIDE 逐项走查这张图。示例是几乎每个产品都有的功能:用户上传头像。
四个问题
一个实用的威胁模型要回答四个问题,它们因 Adam Shostack 和 Threat Modeling Manifesto 而广为人知:
- 我们在做什么?
- 可能会出什么问题?
- 我们打算怎么应对?
- 我们做得够好吗?
数据流图回答第一个问题,STRIDE 帮助回答第二个。附在图上的便签和工单回答第三个,几周后的一次复查回答第四个。
数据流图的元素
| 元素 | 是什么 | 常见形状 |
|---|---|---|
| 外部实体 | 你无法控制的人或系统:用户、合作方的 API | 矩形 |
| 处理过程 | 你运行的、负责转换或传递数据的代码 | 圆形或圆角形状 |
| 数据存储 | 数据静止存放的地方:数据库、存储桶、队列、日志 | 两条平行线或圆柱体 |
| 数据流 | 在两个元素之间流动的数据 | 标注所携带数据的箭头 |
| 信任边界 | 信任级别发生变化的地方 | 虚线,或包围元素的区域 |
有两条规则能让 DFD 真正服务于安全。第一,给每条数据流标注它携带的数据,而不只是协议:photo + session token 告诉评审者的信息远多于 HTTPS。第二,永远不要把两个数据存储或两个外部实体直接相连:数据只能经过处理过程流动,而那里正是你可以检查数据的地方。
示例:上传头像
用户在浏览器中上传头像。Web API 校验用户会话,并把原始文件存入一个私有存储桶。上传处理服务取出文件,把字节发送给第三方恶意软件扫描 API,重新编码图片、去除元数据,把干净的版本写回存储桶,并把照片 URL 记录到用户数据库中。
共有四个信任区域:互联网、我们的应用层、我们的数据层,以及第三方扫描服务。画成 DFD 如下:
图表内容
- Trust zone: internet
- Trust zone: app tier
- Trust zone: data tier
- Trust zone: third party
- User's browser
- Web API
- Upload processor
- Photo bucket (private)
- Users DB
- Malware scan API
- photo + session token
- raw photo
- clean image, no metadata
- file bytes
- verdict
- photo URL + status
数一数跨越区域边界的数据流:浏览器到 API,API 到存储桶,存储桶到处理服务再返回,处理服务到扫描服务再返回,处理服务到数据库。这些地方最值得你花时间。
用 STRIDE 走查这张图
STRIDE 列出了六类威胁。Microsoft 的“按元素 STRIDE”(STRIDE per element)方法只针对每个元素检查适用于其类型的威胁:
| 元素类型 | S | T | R | I | D | E |
|---|---|---|---|---|---|---|
| 外部实体 | ✓ | ✓ | ||||
| 处理过程 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 数据存储 | ✓ | ✓(日志) | ✓ | ✓ | ||
| 数据流 | ✓ | ✓ | ✓ |
六个字母分别代表仿冒(Spoofing)、篡改(Tampering)、抵赖(Repudiation)、信息泄露(Information disclosure)、拒绝服务(Denial of service)和权限提升(Elevation of privilege)。对上传功能逐个元素走查,会得到类似下面的清单:
- 仿冒(浏览器)。 有人用窃取或伪造的会话令牌,以他人名义上传照片。缓解措施:每个请求都校验令牌,让会话保持短时有效,并把对象键绑定到已认证用户的 ID,绝不使用请求中传来的值。
- 篡改(存储桶到处理服务)。 名为
photo.jpg的文件其实完全是别的东西。缓解措施:检查文件的真实格式,而不是文件名或声明的类型,并在其他任何人看到之前重新编码每张图片。 - 抵赖(API)。 用户否认上传过一张冒犯性图片,而没有任何证据能证明事实并非如此。缓解措施:每次上传都记录用户 ID、对象键、时间和客户端地址。
- 信息泄露(存储桶)。 存储桶允许公开列出内容,或者照片的 EXIF 元数据中仍保留着 GPS 坐标。缓解措施:使用私有存储桶和短时有效的签名 URL,并在重新编码时去除元数据。
- 拒绝服务(API 和处理服务)。 超大文件,或解压后体积巨大的图片,会耗尽内存。缓解措施:限制文件大小和像素数,并为处理服务设置超时。
- 权限提升(处理服务)。 精心构造的文件利用图片库中的漏洞,以处理服务的权限执行代码。缓解措施:在沙箱中运行处理服务,除了它自己的存储桶前缀和唯一的一张表之外,不允许它访问任何东西。
把威胁以简短、编号的条目写进受影响元素旁边的便签里,并把每一条链接到跟踪修复的工单。这样,这张图同时也是评审的总结。
威胁建模图的规则
- 画功能,而不是整个公司。 每个功能或每个服务一张 DFD。什么都画的图,容不下真正重要的细节。
- 标出认证发生在哪里。 在每条跨越边界的数据流上,标记负责校验身份的处理过程。
- 把没人演示的路径也画进去。 管理工具、支持人员访问、批处理作业和备份同样会跨越边界。
- 保持同一个细节层级。 上下文级 DFD 把系统画成一个处理过程;1 级 DFD 则把它拆分成主要的处理过程。每张图只选其中一种。
- 随功能一起做版本管理。 设计变了,威胁也会变。在与设计文档相同的 pull request 中更新这张图。
专用工具还是共享画布
OWASP Threat Dragon 和 Microsoft Threat Modeling Tool 等免费工具专为这项工作打造,能为每个元素建议可能的威胁。由安全团队主导流程时,它们很好用。
如果由开发这个功能的人在设计阶段一起画图,共享画布更合适:DFD 就放在架构图旁边,所有人实时编辑,便签始终附着在对应的元素上。无论选哪种方式,真正有价值的都是围绕这张图展开的讨论。
在 Archboard 中构建
- 使用 Google 或 GitHub 登录,并新建一张图。
- 为每个信任区域添加一个基础节点包中的框架,并以区域命名,例如
Trust zone: app tier。 - 用基础节点包的方框节点表示外部实体,用基础节点包的椭圆形状表示处理过程,再从数据节点包中添加数据存储(对象存储库、Postgres)。
- 连接端口,并给每条连线标注它携带的数据。
- 在受影响的元素旁边添加一个便签,列出每个威胁的 STRIDE 字母、编号和工单。
这个示例使用了 10 个图表节点,外加三个列出七个威胁的便签,在 Free 套餐的限制之内。网络层面的安全可以参考零信任架构图模板;梳理个人数据可以参考GDPR 数据映射图使用场景。
常见问题
数据流图和架构图是一回事吗? 不是。架构图展示组件以及它们如何连接;DFD 展示数据如何流动、信任在哪里发生变化。两者经常有相同的方框,但 DFD 是为回答安全问题而画的。
用 STRIDE 还是别的方法? 对软件功能来说,STRIDE 是不错的默认选择。LINDDUN 专注于隐私威胁,PASTA 则是面向大型项目、以风险为中心的流程。对所有这些方法来说,这张图都是相同的起点。
应该请哪些人参加? 开发这个功能的工程师、将来负责运维它的人,以及一名安全工程师(如果你们有的话)。熟悉代码的人负责找出威胁;安全工程师负责让走查不流于形式。
需要多长时间? 像照片上传这样的功能,通常一小时:15 分钟画图,30 分钟用 STRIDE 走查,15 分钟商定修复方案和负责人。
多久应该更新一次模型? 每当有数据流跨越新的边界、出现新的数据存储,或者功能改变了谁能做什么时。