4. API资产发现与梳理
API资产发现与梳理是API安全治理的起点。如果连系统对外暴露了哪些API、每个API由谁维护、当前版本是什么、是否仍在被调用都不清楚,后续的鉴权测试、越权检测、限流防护和风险处置都无从谈起。本节围绕四条主线展开:从代码与文档中主动发现端点、解析Swagger/OpenAPI描述文件、从真实流量中反向还原API清单,以及在此基础上识别影子API与僵尸API。
一、API端点发现的三种来源
端点发现的目标是形成一份“候选API清单”,它通常来自三个互补的来源。第一是代码与框架路由:Spring Boot的@RequestMapping、@GetMapping,Flask的@app.route,Express的router.get,以及gRPC的proto服务定义,都能静态提取出路径、方法和参数结构。第二是接口文档:Swagger/OpenAPI、RAML、Postman Collection等描述文件本身就是结构化的端点清单。第三是运行流量:网关、Nginx、Service Mesh或APM采集到的真实请求,能反映实际被访问的端点,包括未写入文档的部分。
三者之间存在明显差异:代码里有但文档没写的,往往是内部接口或调试接口;文档里有但流量中从未出现的,可能是废弃接口;流量中出现但代码和文档都没有的,则高度疑似影子API。因此端点发现不应只依赖单一来源,而应做交叉比对。
二、Swagger/OpenAPI文档解析
OpenAPI规范(Swagger 2.0与OpenAPI 3.x)以JSON或YAML描述每个端点的路径、方法、参数、请求体、响应码和安全方案。解析时重点关注以下字段:paths下的每个路径与方法组合构成一个端点;parameters中的in字段区分query、path、header、cookie参数;requestBody与schema描述数据结构;security声明该端点是否需要认证。这些信息直接决定后续测试用例的构造方式。
实际解析中常见几类问题:一是文档与实现不一致,例如文档声明GET /users/{id}需要Bearer Token,但代码实际未校验;二是servers字段配置了多个环境地址,需要区分生产与测试;三是$ref引用导致结构嵌套,需要递归展开。下面是一段使用Python解析OpenAPI并提取端点清单的示例:
import json, yaml
def load_spec(path):
with open(path) as f:
return yaml.safe_load(f) if path.endswith((".yaml", ".yml")) else json.load(f)
def extract_endpoints(spec):
endpoints = []
for path, methods in spec.get("paths", {}).items():
for method, detail in methods.items():
if method.lower() not in ("get", "post", "put", "delete", "patch"):
continue
params = [p.get("name") for p in detail.get("parameters", [])]
auth = bool(detail.get("security", spec.get("security", [])))
endpoints.append({
"method": method.upper(),
"path": path,
"params": params,
"auth_required": auth,
"tags": detail.get("tags", [])
})
return endpoints
spec = load_spec("openapi.yaml")
for ep in extract_endpoints(spec):
print(ep)
输出结果可直接作为资产台账的初始数据。示例中auth_required字段尤其重要,它标记了文档声称需要认证的端点,后续可与流量中实际携带凭证的比例做对比,发现“文档要求认证但实际匿名可访问”的高风险接口。
三、流量分析还原真实API
流量分析解决的是“实际发生了什么”。在网关或镜像端口采集HTTP请求后,按Host + Method + 路径模板聚合。路径模板化是关键步骤:把/api/v1/users/1024和/api/v1/users/2048归并为/api/v1/users/{id},否则会产生海量伪端点。常用做法是用正则替换数字、UUID、长哈希等动态段。
聚合后为每个端点统计:调用次数、独立来源IP数、平均响应码分布、是否携带Authorization头、请求体大小范围、最近一次调用时间。这些指标是后续识别影子与僵尸API的核心依据。例如,一个端点近90天调用次数为0,基本可判定为僵尸API;一个端点调用量很高但从未出现在任何文档中,则属于影子API。
四、影子API与僵尸API识别
影子API指未纳入文档、未经过安全评审、但实际可被访问的接口。常见成因包括:开发人员为联调临时开放的调试接口、旧版本未下线但仍在路由中的接口、第三方SDK自动注册的回调地址、以及微服务间未走网关的内部接口被误暴露。识别影子API的核心判据是“流量中存在、但文档与代码清单中均不存在”,同时结合响应码为2xx、返回体包含业务数据等特征提高置信度。
僵尸API指已停止维护、文档可能仍保留、但实际已无正常业务调用的接口。它们往往运行在旧版本框架上,缺少最新安全补丁,一旦被攻击者发现即可利用。识别判据是“文档或代码中存在、但近N天流量为0”。N的取值需结合业务周期,一般取30至90天;对账务、报表类低频接口可适当放宽。
实际梳理中建议形成一张资产台账,字段包括:端点路径、方法、来源(代码/文档/流量)、认证要求、最近调用时间、调用量、归属服务、负责人、风险标签。下表给出一个简化的示例片段:
| 方法 | 路径 | 来源 | 认证 | 近30天调用 | 标签 |
|---|---|---|---|---|---|
| GET | /api/v1/users/{id} | 代码+文档+流量 | Bearer | 128,430 | 正常 |
| POST | /api/v1/orders | 代码+文档+流量 | Bearer | 56,210 | 正常 |
| GET | /api/v1/debug/env | 仅流量 | 无 | 37 | 影子API |
| GET | /api/v0/report/export | 文档+代码 | Bearer | 0 | 僵尸API |
| POST | /internal/cache/flush | 仅代码 | 无 | 0 | 待确认 |
五、梳理结果的持续维护
API资产不是一次性清单,而是持续变化的对象。建议将端点发现流程接入CI/CD:每次代码合并时重新提取路由,与上一版文档和流量基线做差异比对,新增或变更的端点自动进入评审队列。同时,网关侧定期导出流量聚合结果,与资产台账做双向校验,确保“台账中的端点都在流量中有对应,流量中的端点都在台账中有归属”。只有形成这种闭环,影子API和僵尸API才不会在两次梳理之间悄悄积累。