STRIDE 威胁建模
QuickBite 要上线在线点餐功能。顾客挑选食物、付款,然后等餐厅确认订单。
正常流程很清晰:顾客提交订单,厨房收到订单,应用发送状态更新。
但安全考虑要问一个不一样的问题:在我们把它做出来之前,会出什么问题?
这个问题有个名字,叫**威胁建模**。你审视一个设计,想象它可能被怎样滥用,然后决定代码需要做哪些防范。
威胁建模发生在漏洞报告之前
代码审查看的是已经存在的代码。
威胁建模看的是还处在改动成本很低阶段的功能。
STRIDE 清单
STRIDE 是一个助记词,代表攻击者可能捣乱系统的六种方式。这几个字母分别代表:
| 字母 | 威胁 | 通俗提问 |
|---|---|---|
| S | 仿冒(Spoofing) | 有人能冒充别人吗? |
| T | 篡改(Tampering) | 有人能更改不该由他们更改的数据吗? |
| R | 抵赖(Repudiation) | 有人能因为没有记录而否认自己做过某事吗? |
| I | 信息泄露(Information disclosure) | 有人能看到不该看到的数据吗? |
| D | 拒绝服务(Denial of service) | 有人能让应用对其他人无法正常工作吗? |
| E | 权限提升(Elevation of privilege) | 有人能做超出自己角色权限的事吗? |
课程里有一句话说得很实用:STRIDE 给你六种思路,让你去想攻击者可能怎么捣乱你的系统。
我们把这六个问题拿来问一问 QuickBite。
一旦把这几个字母换成通俗的问题,它们就没那么吓人了:有人能冒充、更改、否认、读取、阻断,或者做超出权限的事吗?
用 STRIDE 走一遍 QuickBite
先从一个功能流程开始:
- 顾客创建订单。
- 服务端计算总价。
- 顾客付款。
- 餐厅接受或拒绝订单。
- 顾客收到状态更新。
现在把 STRIDE 的六个问题拿来对照这个流程逐一发问。
| 威胁 | QuickBite 示例 | 应对决定 |
|---|---|---|
| 仿冒 | 某个请求冒充成另一位顾客。 | 使用已登录的会话身份,而不是请求体里的用户 id。 |
| 篡改 | 浏览器发送了一个更便宜的价格。 | 由服务端根据菜单数据计算价格。 |
| 抵赖 | 餐厅声称自己从未拒绝过某个订单。 | 记录谁在什么时候修改了订单状态。 |
| 信息泄露 | 一位顾客能读到另一位顾客的订单。 | 返回订单详情前先检查归属权。 |
| 拒绝服务 | 某个用户发送了成千上万个订单。 | 加上速率限制和请求体大小限制。 |
| 权限提升 | 顾客调用了本应仅限餐厅使用的接口。 | 在服务端检查角色和餐厅所属关系。 |
这张表本身就已经很有用了。它告诉团队,在这个功能上线前,哪些检查是必须存在的。
威胁模型只有在每个威胁都落实成一个具体决定时才有意义。
然后在旁边写下应对决定。一个担忧只有在有人能把答案做出来或测出来时,才真正有用。
潜在威胁建模
潜在威胁建模从更宽的范围入手。你审视的不是单个功能,而是整个系统或系统中很大的一部分。
对 QuickBite 来说,这可能意味着:
- 顾客登录后能做的所有事情
- 餐厅老板能做的所有事情
- 整条支付链路
- 订单数据离开应用的每一个出口
当你接手一个已有应用、启动一个新系统,或者想找出老设计里的问题时,这种方式很有用。
风险在于范围失控。"整个应用"很容易变成一场冗长的会议和一屋子疲惫的人。要选一个大家能明确指出边界的范围。
开始前先划定范围
潜在威胁建模需要一个固定的范围和一个固定的时长。
少了任何一项,这个过程就会一直膨胀,直到没人再能做出任何决定。
把边界控制在能看得清的范围内。"支付链路"是有用的范围,"整个应用"通常只会变成一团迷雾。
动手试一试
QuickBite 新增了一个功能:顾客可以对已完成的订单留下评价,餐厅可以回复。
用 STRIDE 把它走一遍。针对每个字母,说出一个可能出错的地方,以及对应的检查手段。
对照你的答案
| 字母 | 可能出错的地方 | 检查手段 |
|---|---|---|
| S 仿冒 | 有人冒充另一位顾客发布评价 | 服务端从会话中获取作者身份,绝不从请求体中获取 |
| T 篡改 | 餐厅事后修改了某条评价的内容或评分 | 只有作者本人能编辑自己的评价,回复是独立的记录 |
| R 抵赖 | 餐厅否认自己发布过一条辱骂性回复 | 记录谁在什么时候写了什么内容,带上账号 id |
| I 信息泄露 | 评价里暴露了顾客的全名或地址 | 在存储数据前先确定哪些字段是公开的,只返回这些字段 |
| D 拒绝服务 | 有人发布了成千上万条评价,或者一条长达一兆字节的评价 | 对文本长度做限制,对接口做速率限制 |
| E 权限提升 | 顾客以餐厅的身份进行了回复 | 回复接口检查该账号是否确实拥有那家餐厅 |
有两点值得注意。R(抵赖)是大家最容易跳过的一项,因为在有人提出争议之前一切看起来都好好的,而到了那时候,记录要么存在,要么不存在,没有补救的余地。
而S(仿冒)有一个能一劳永逸解决问题的办法:身份信息只从会话中取,绝不取自调用方发来的字段。如果一条评价自带一个 authorId 字段,那任何人都能冒用别人的名字来签署这条评价。
接下来去哪儿
STRIDE 给了你这六个问题。下一章要改变的是审视的尺度。
不再是扫视整个系统,基于功能的威胁建模把这些问题一次只落到一个功能上。这是大多数开发者能在日常规划中直接用上的版本。

