威胁建模 STRIDE 是系统设计阶段识别安全风险的结构化方法,但很多团队止步于「画一张图、列几条风险」,模型与代码脱节、评审后无人跟进。本文给出威胁建模 STRIDE 的工程化落地路径:用 pytm 把数据流图写成 Python 代码,自动匹配威胁规则并输出威胁清单、DFD 图与序列图,最终纳入 CI 流水线,让威胁模型随架构变更持续更新。
适用场景
- 新系统立项或架构评审前,需要系统化输出威胁清单与整改项,而不是靠个人经验零散提示。
- 既有系统迭代变更(新增接口、新增第三方集成)时,需要快速评估变更带来的新增风险面。
- 等保 2.0、ISO 27001 与安全开发生命周期(SDLC)要求提供威胁建模文档与整改记录。
- 外包或采购系统验收时,需要独立复现对方的数据流与信任边界,验证其安全设计假设。
前置条件
- Python 3.9 及以上版本,可安装第三方包。
- 安装 pytm(威胁建模引擎)与 Graphviz(生成 DFD 与序列图)。
- 一份可用的系统架构信息:外部实体、服务进程、数据存储、数据流方向与信任边界。
- 版本控制仓库(Git),用于把威胁模型与代码同仓管理、在 CI 中运行。
- 明确的风险评分标准(CVSS 或 DREAD),用于对自动生成的威胁做优先级排序。
原理说明
STRIDE 把威胁归纳为六类,每一类对应一个安全属性被破坏的情形:
- Spoofing 假冒:身份可被伪造,破坏认证属性。
- Tampering 篡改:数据或代码可被未授权修改,破坏完整性。
- Repudiation 抵赖:操作无法追溯,破坏不可否认性。
- Information Disclosure 信息泄露:数据被未授权读取,破坏机密性。
- Denial of Service 拒绝服务:可用性被破坏。
- Elevation of Privilege 权限提升:低权限主体获得高权限,破坏授权。
STRIDE 的分析载体是数据流图(DFD),其四类元素与 STRIDE 的对应关系是固定的:外部实体主要关注假冒与抵赖,进程关注全部六类,数据存储关注篡改、信息泄露与拒绝服务,数据流关注篡改、信息泄露与拒绝服务。信任边界(Boundary)之间的跨越点,是风险最集中的位置。
pytm 把这套方法代码化:用 Python 类描述 DFD 元素,并为元素设置安全属性(是否加密、是否校验输入、是否鉴权、是否加固等);pytm 内置一份威胁规则库(threatlib),逐条以条件表达式匹配元素属性,满足条件即输出对应威胁。这样威胁建模不再是「一次性文档」,而是可版本控制、可自动运行、可回归的代码资产。
操作步骤
步骤一:安装环境
# 安装 pytm 与 Graphviz
python3 -m pip install pytm
sudo apt update && sudo apt install -y graphviz
# 验证
python3 -c "import pytm; print(pytm.__version__ if hasattr(pytm,'__version__') else 'pytm ok')"
dot -V
步骤二:用 Python 描述数据流图
新建 tm/TM.py,按「边界 → 元素 → 数据流」的顺序定义。注意把安全属性显式写全,属性缺失会导致条件不成立、威胁被漏报。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""pytm 威胁模型:电商下单系统"""
from pytm import TM, Actor, Boundary, Dataflow, Datastore, Server
tm = TM("电商下单系统")
tm.description = "覆盖用户下单、订单 API 与订单库的数据流模型"
tm.isOrdered = True
# 1) 定义信任边界
internet = Boundary("Internet")
dmz = Boundary("DMZ")
internal = Boundary("内部网络")
# 2) 定义元素及其安全属性
user = Actor("外部用户", inBoundary=internet)
web = Server("Web 前端", inBoundary=dmz)
web.OS = "Ubuntu 22.04"
web.isHardened = True # 已做基线加固
web.validatesInput = True # 已做输入校验
web.sanitizesInput = True
api = Server("订单 API", inBoundary=internal)
api.OS = "Ubuntu 22.04"
api.isHardened = True
api.authenticated = True # 调用方需认证
api.authorizes = True # 已做权限校验
api.validatesInput = True
api.sanitizesInput = True
api.usesEnvironmentVariables = True
db = Datastore("订单数据库", inBoundary=internal)
db.storesSensitiveData = True # 存有个人与支付信息
db.isEncrypted = True
db.isSQL = True
db.validatesInput = True
# 3) 定义数据流
f1 = Dataflow(user, web, "下单请求")
f1.protocol = "HTTPS"
f1.isEncrypted = True
f2 = Dataflow(web, api, "内部 API 调用")
f2.protocol = "HTTPS"
f2.isEncrypted = True
f3 = Dataflow(api, db, "SQL 读写")
f3.protocol = "PostgreSQL"
f3.isEncrypted = True
tm.process()
步骤三:生成 DFD、序列图与威胁报告
cd tm
# 生成数据流图(需要 graphviz 的 dot)
python3 TM.py --dfd | dot -Tpng -o dfd.png
# 生成序列图,便于与开发对齐调用顺序
python3 TM.py --report seq | dot -Tpng -o seq.png
# 输出威胁报告(纯文本,无需 graphviz)
python3 TM.py --report threats | tee threats.txt
# 输出 JSON,便于后续自动统计与入库
python3 TM.py --json threats.json
jq 'length' threats.json
威胁报告每条包含编号(SID)、威胁描述、命中条件、严重度与整改建议。统计条目数可用:
# 按严重度分组统计
jq -r '.[].severity' threats.json | sort | uniq -c | sort -rn
步骤四:补充自定义威胁规则
内置规则覆盖通用场景,业务特有风险需自建规则。规则以 JSON 数组给出,条件字段是作用于目标元素的 Python 表达式。
cat > custom_threats.json <<'JSON'
[
{
"SID": "BIZ01",
"target": ["Server"],
"description": "订单金额由前端传入且服务端未二次校验",
"details": "攻击者可篡改下单请求中的金额或折扣字段,造成资损。",
"Likelihood Of Attack": "High",
"severity": "High",
"condition": "target.validatesInput is False or target.authorizes is False",
"mitigations": "服务端按商品 SKU 重新计算价格与优惠,禁止信任前端金额字段;对关键字段做签名校验。"
},
{
"SID": "BIZ02",
"target": ["Dataflow"],
"description": "跨信任边界的数据流未加密",
"details": "DMZ 到内部网络的调用若为明文,可被横向嗅探。",
"Likelihood Of Attack": "Medium",
"severity": "High",
"condition": "target.isEncrypted is False",
"mitigations": "全链路启用 TLS,内网服务间使用 mTLS 或服务网格加密。"
}
]
JSON
# 加载自定义规则(默认会与内置规则合并)
python3 TM.py --threats custom_threats.json --report threats | head -60
步骤五:风险排序与整改闭环
自动生成的威胁条目通常较多,直接全部修复不现实。可按「严重度 × 可达性」排序,优先处理跨信任边界且影响机密性与完整性的条目。建议把每条威胁映射到责任人、整改方案与截止时间,形成威胁跟踪表;整改后同步更新元素属性(如把 isEncrypted 置为 True),再次运行确认该威胁消失,验证整改有效。
步骤六:纳入 CI 流水线
# .github/workflows/threatmodel.yml
name: Threat Model
on: [push, pull_request]
jobs:
pytm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install pytm
- name: Generate threat report
run: |
cd tm
python3 TM.py --report threats > threats.txt
python3 TM.py --json threats.json
- name: Fail on high severity threats
run: |
cd tm
HIGH=$(jq '[.[] | select(.severity=="High")] | length' threats.json)
echo "High severity threats: $HIGH"
test "$HIGH" -eq 0
- uses: actions/upload-artifact@v4
with:
name: threat-report
path: tm/threats.txt
配置验证
python3 TM.py --dfd | dot -Tpng -o dfd.png成功生成图片,且图中元素与边界与架构一致。python3 TM.py --report threats输出多条威胁,且包含自定义规则的 SID(如 BIZ01)。- 反向验证:把某个元素的
isEncrypted由True改为False并重跑,报告应新增跨边界明文流的威胁条目,证明规则确实生效。 - CI 验证:提交一次含高危威胁的改动,流水线应在「Fail on high severity threats」步骤失败。
常见问题(FAQ)
Q1:运行 pytm 报「dot: command not found」或图生成失败怎么办?
pytm 生成 DFD 与序列图依赖 Graphviz 提供的 dot 可执行程序,仅安装 Python 包不够。需单独安装 Graphviz 并确保 dot 在 PATH 中。若仅需威胁报告,可跳过 DFD 生成。
# Debian / Ubuntu
sudo apt install -y graphviz
# RHEL / Rocky
sudo dnf install -y graphviz
# 验证 dot 可用
which dot && dot -V
Q2:自动生成的威胁条目过多,评审时无法逐条讨论,如何落地?
建议做三层收敛:一是按目标元素筛选,只讨论跨信任边界的进程与数据流;二是按严重度排序并设定门槛(如只处理 High 与 Medium),其余登记为已知接受风险;三是把通用缓解措施标准化,例如「全链路 TLS」「统一输入校验」「统一鉴权中间件」,一次配置即可消除大批同类威胁。收敛后只保留需要产品与架构决策的少数条目进入评审。
Q3:威胁模型会不会因为属性写错而漏报威胁?
会,这是 pytm 类方法的主要局限:安全属性写得越乐观,漏报越多。缓解方式是三条纪律:元素属性必须由实际实现而非设计意图填写;把威胁模型产出纳入代码评审,由架构与安全双方共同确认;定期用反向验证(故意把属性置为不安全,确认威胁能被检出)检查规则是否仍然有效。
总结
威胁建模 STRIDE 的工程化落地,关键是把「文档」变成「代码」。pytm 让数据流图、威胁清单与整改状态都进入版本控制,可以随架构变更自动重跑,也能在 CI 中设置严重度门槛做增量拦截。实践顺序建议为:先定义边界与元素的真实安全属性,再运行内置规则得到基线清单,随后补充业务特有的自定义规则,最后把风险排序结果转化为可跟踪的整改任务并回归验证。这样威胁建模才真正进入研发流程,而不是评审会上的一次性材料。