第三章:数据收集与预处理——架构图解析、数据流提取、资产清单生成、API接口数据采集、数据清洗与标准化

好,咱们进入实战环节的第一个硬骨头:数据收集与预处理。

你想想看,威胁建模这事儿,说白了就是「先搞清楚你有什么,再想谁会搞你,最后怎么防」。但第一步「搞清楚你有什么」,往往最容易被忽视。我见过太多团队,一上来就画威胁树、分析攻击路径,结果连系统里跑了哪些API、数据流往哪儿走都没摸清。嗯,这就像打仗不看地图,直接冲锋——勇气可嘉,但大概率要吃亏。

3.1 架构图解析:从「图」到「数据」的第一步

我个人习惯,拿到一个系统,第一件事不是读文档,而是看架构图。但架构图有个问题——它是给人看的,不是给机器看的。你想想,一张PNG图片里画了几个框、几条线,机器怎么知道哪个框是「数据库」、哪条线是「HTTPS流量」?

所以,我们需要把架构图「解析」成结构化数据。这里我推荐两种主流方式:

  • 基于标注的解析:如果架构图是用Draw.io、Visio这类工具画的,通常能导出XML或JSON格式。我们可以直接解析这些文件,提取节点和连线信息。
  • 基于OCR+图像识别:如果只有一张截图,那就得用OCR识别文字,再用目标检测模型识别图标(比如数据库图标、服务器图标)。我在项目中遇到过,某客户给了张模糊的PPT截图,OCR识别率只有60%,后来我让他们重新导出源文件——省了三天功夫。

核心思路:架构图解析的目标是生成一个「节点-边」的图结构。节点代表组件(服务、数据库、负载均衡器等),边代表通信关系(协议、端口、数据流向)。

举个例子,假设我们有一个微服务架构图,解析后得到的数据结构大概是这样的:

{
  "nodes": [
    {"id": "svc-auth", "type": "service", "label": "认证服务"},
    {"id": "db-user", "type": "database", "label": "用户数据库"},
    {"id": "gw-api", "type": "gateway", "label": "API网关"}
  ],
  "edges": [
    {"source": "gw-api", "target": "svc-auth", "protocol": "HTTP", "port": 8080},
    {"source": "svc-auth", "target": "db-user", "protocol": "JDBC", "port": 3306}
  ]
}

有了这个结构,后续的威胁分析才能自动化。否则,你连系统里有多少个组件都数不清,谈何威胁建模?

3.2 数据流提取:别漏了「隐式」的流

架构图解析完了,接下来要提取数据流。这里有个坑——很多人只关注「显式」的数据流,比如HTTP请求、数据库查询。但真正的威胁往往藏在「隐式」的数据流里。

什么是隐式数据流?我举几个例子:

  • 日志输出:应用把敏感信息打到日志里,然后日志被采集到ELK——这算不算数据流?当然算!而且经常出问题。
  • 缓存同步:Redis集群之间的数据同步,如果没加密,中间人就能偷数据。
  • Sidecar代理:Service Mesh模式下,流量先经过Envoy再进业务容器——这个「经过」的过程,数据流其实多了一跳。

我曾经在审计一个金融系统时,发现架构图上只画了「前端→后端→数据库」三条线。但实际跑起来,还有日志流、监控流、配置下发流……最后我帮他们补了7条隐式数据流,其中一条就是「日志服务器明文传输信用卡号」——嗯,这要是被攻破了,后果不堪设想。

我的建议:提取数据流时,别只看架构图。去翻翻配置文件、看看代码里的HTTP客户端调用、查一下消息队列的Topic列表。这些地方往往藏着「惊喜」。

3.3 资产清单生成:从「组件」到「资产」的映射

数据流提取完了,接下来要生成资产清单。注意,这里的「资产」不是指服务器IP,而是指需要保护的东西——比如数据库里的用户表、API接口的认证逻辑、甚至是一个配置文件。

我个人习惯把资产分为三类:

资产类型 示例 威胁关注点
数据资产 用户个人信息、交易记录、密钥 泄露、篡改、丢失
功能资产 登录接口、支付接口、文件上传 越权、注入、滥用
基础设施资产 数据库、消息队列、DNS服务器 可用性、配置错误

生成资产清单时,我通常会写一个自动化脚本,从架构图解析结果里自动提取组件,然后根据组件类型打标签。比如:

def generate_asset_inventory(nodes, edges):
    assets = []
    for node in nodes:
        if node['type'] == 'database':
            assets.append({
                'name': node['label'],
                'type': 'data_asset',
                'data_sensitivity': 'high',  # 默认高敏感,后续人工复核
                'related_flows': [e for e in edges if e['target'] == node['id']]
            })
        elif node['type'] == 'service':
            assets.append({
                'name': node['label'],
                'type': 'function_asset',
                'exposed_apis': extract_apis_from_service(node['id']),
                'auth_required': True
            })
    return assets

注意,自动化生成的清单只能当「初稿」。我建议你一定要人工复核一遍——特别是那些标记为「低敏感」的资产,往往最容易出问题。

3.4 API接口数据采集:别只盯着RESTful

API接口是攻击者的主要入口,也是威胁建模的重点。但很多人采集API时,只关注RESTful接口,忽略了其他类型的API。

我列一下常见的API类型,你对照看看有没有遗漏:

  • RESTful API:最常见,通常通过Swagger/OpenAPI文档采集。
  • GraphQL API:一个端点搞定所有查询,但攻击面更大——我见过有人通过GraphQL的嵌套查询拖库。
  • gRPC API:基于Protobuf,二进制协议,传统扫描工具很难发现。
  • WebSocket API:长连接,实时通信,容易被安全团队忽略。
  • 消息队列API:Kafka、RabbitMQ的Topic,如果暴露了生产/消费权限,后果很严重。

采集API数据时,我推荐用两种方式结合:

  1. 静态分析:从代码仓库里扫描OpenAPI文档、Protobuf定义文件、GraphQL Schema。
  2. 动态抓取:在测试环境跑一遍功能用例,用抓包工具记录所有API调用。

注意:动态抓取时,一定要确认你抓到了「所有」API调用。我曾经遇到过,某个系统有「管理员专用API」,测试用例没覆盖到,结果那个API没有任何安全防护——嗯,你懂的。

3.5 数据清洗与标准化:把「脏数据」变成「干净数据」

数据收集完了,但你会发现——数据很「脏」。比如:

  • 同一个数据库,架构图上叫「user_db」,代码里叫「users_db」,配置文件里叫「mysql-01」。
  • API路径,有人写「/api/v1/login」,有人写「/v1/api/login」。
  • 协议字段,有的写「HTTP」,有的写「http」,有的写「HTTP/1.1」。

如果不做清洗和标准化,后续的威胁分析模型根本没法跑——因为同一个东西被当成了三个不同的东西。

我常用的清洗步骤:

  1. 字段对齐:把所有字段名统一成小写+下划线格式(比如 user_db、api_path)。
  2. 值归一化:协议统一成大写(HTTP、HTTPS、JDBC),端口统一成整数。
  3. 实体消歧:用别名表把不同来源的同一实体映射到唯一ID。比如:
alias_map = {
    'user_db': 'db-001',
    'users_db': 'db-001',
    'mysql-01': 'db-001'
}
  1. 格式校验:检查IP地址格式、端口范围、URL合法性。不合法的数据直接标记为「待人工确认」。

标准化之后的数据,我会存成统一的JSON格式,方便后续的威胁分析引擎直接消费。举个例子:

{
  "asset_id": "db-001",
  "asset_name": "用户数据库",
  "asset_type": "database",
  "protocols": ["JDBC"],
  "ports": [3306],
  "data_sensitivity": "high",
  "related_apis": ["/api/v1/user/profile", "/api/v1/user/login"],
  "data_flows": [
    {"source": "svc-auth", "target": "db-001", "protocol": "JDBC", "data_type": "user_credentials"}
  ]
}

这一步做完,你的数据才算「能用」。否则,后面所有的自动化分析都是空中楼阁。

一个小技巧:清洗脚本里一定要加「异常告警」。比如,如果某个字段的标准化失败率超过10%,就发告警——这通常意味着数据源有问题,需要人工介入。我在项目中靠这个规则,提前发现了三次数据采集脚本的Bug。

小结

数据收集与预处理,听起来很枯燥,但它是整个威胁建模自动化的地基。地基没打好,上面盖的房子再漂亮也得塌。

我个人总结了一个「三不放过」原则:

  • 架构图没解析完,不放过;
  • 隐式数据流没提取,不放过;
  • 数据没标准化,不放过。

下一章,咱们聊聊怎么用这些「干净数据」来自动生成威胁模型。嗯,那才是真正有意思的部分。