name: sensitive-info-exposure description: 敏感信息泄露检测 — 检测 API 响应、错误回显、日志、响应头、备份/元数据文件中是否暴露未脱敏的 PII / 凭证 / 内部地址 / 系统配置;适用于所有返回业务数据或错误信息的端点。 when-to-use: 当需要检查接口响应、日志回显、导出内容是否暴露未脱敏的 PII/认证/财务/系统配置信息时 allowed-tools: bash,read_file,list_files,rg user-invocable: false
敏感信息泄露检测
成因引用
敏感信息泄露成因:source(任何请求)→ sink(API 响应 / 页面响应 / 错误消息 / 日志 / Set-Cookie / 响应头 / robots.txt / sitemap / HTML 注释 / 缓存 / 备份文件 / 元数据端点 / swagger / git 元数据)。PII / 凭证 / 内部地址 / SQL 错误原文明文返回,无脱敏或最小化暴露。详见同根目录 pentest/web-security-testing/SKILL.md 漏洞成因图谱 · 敏感信息泄露行(不在本 skill 重复成因)。
关键 sink 形态:sink 不仅是"接口返回字段"——响应头、Set-Cookie、错误堆栈、日志查看接口、HTML 注释、JSON-LD、robots.txt 暴露的路径都是同一 sink 范畴。任何"服务端把信息发到客户端可见之处"都属 sink,按"信息是否真实敏感 + 接收方是否应见"判定,而非按字段名筛选。
触发线索(基线检查项)
以下是已知的常见敏感信息泄露触发线索,作为基线起点而非必检硬清单:
- 适用且已完成 → 标注
[x] done - 明确不适用 → 标注
[-] n/a (原因) - 基线未列出但实际发现 → 新增条目并标注
[+] added (来源)
基线触发线索按"sink 语义"分类(不按业务命名):
- 业务列表 / 详情 API:用户列表、订单详情、客服记录、审计列表 — 可能含他人 PII(手机号、邮箱、身份证、卡号)
- 错误响应:SQL 错误原文、堆栈跟踪、模板渲染错误 — 泄露内部路径 / 类名 / 数据库结构
- 调试 / 诊断端点:
/debug/*//actuator/*//_info//metrics— 内部信息全集 - 元数据端点:
/swagger.json//api-docs//openapi.yaml— 接口结构 + 敏感字段定义 - 静态文件 / 备份残留:
/.env//.git/*//backup.zip//db.sql//.DS_Store— 凭证 + 源码 - robots.txt / sitemap.xml:暴露内部路径或后台入口
- HTML 注释 / JSON-LD / 模板渲染产物:源码注释含 TODO / 密钥 / 内部 IP / 调试信息
- 响应头:
Server/X-Powered-By/ 自定义X-Internal-*/ Set-Cookie 中含可解码的会话 / 令牌信息 - 日志查看接口:管理后台调试日志中含明文请求体(密码 / token / PII)
- 缓存控制不当:含敏感数据的响应被 CDN / 浏览器缓存
- 代码模式:后端代码出现
c.JSON(user)直接序列化整个 ORM 对象(含 password_hash / secret 字段);或panic/ 错误对象直接String()返回响应
思考检查点
加载本 skill 时按这些问题思考:
- 这个响应的接收方是谁?匿名 / 普通用户 / 低权限角色 / 管理员?字段内容他/她应不应该看到?
- 脱敏是服务端做的还是前端做的?API 返回的是
133****1234还是明文13312341234?前端脱敏不算脱敏。 - 该业务实体(用户 / 订单 / 工单 / 发票 / 合同)的所有相关 API 是否都查过?同实体不同变体(列表 vs 详情 vs 导出)的脱敏可能不一致。
- 信息泄露面除了响应 body,是否还包括响应头、Set-Cookie、错误堆栈、HTML 注释、robots / sitemap、缓存控制?
- 子系统 A 的脱敏方案是否假定可推广到 B/C/D?还是每个子系统都需独立测?
前置条件与安全边界
- 仅在授权环境与测试数据集执行;命中真实敏感数据时立即停止扩展。
- 导出 / 列表类接口仅做小样本验证(如
limit=5),禁止批量拉取。 - 证据留存时必须二次脱敏(如
138****5678),避免二次传播。 - 错误回显 / 堆栈类信息可直接留存(属于系统内部,不含用户 PII)。
- 静态备份残留如包含真实凭证,仅证明可访问性后立即停止下载并通报。
检测步骤
Step 1:基线与敏感面探测
- 从端点账本(
recon-methodology产出的endpoint-ledger.jsonl)查询:- 所有列表 / 详情 / 导出 API(按业务实体分组:用户 / 订单 / 工单 / 发票 / 合同 / 客服 / 日志 / 审计)
- 静态资源命中(
/.env//.git/HEAD//backup.zip//swagger.json//robots.txt//sitemap.xml) - 调试 / 诊断端点(
/debug//actuator//_info//metrics)
- 用合法账号调用每类基线请求,保存完整响应(含 body / headers / Set-Cookie)
- 标记可疑字段(按 PII / 凭证 / 财务 / 内部配置 / 系统标识 五类初分)
Step 2:脱敏与最小化校验
对每个响应字段逐项对照敏感信息基线:
| 类别 | 字段示例 | 期望脱敏形态 |
|---|---|---|
| 身份标识 | 姓名 / 手机号 / 邮箱 / 身份证 / 护照 / 银行卡 | 张* / 133****1234 / 110101********1234 |
| 认证凭证 | 密码哈希 / JWT / session ID / API Key / AccessKey / Authorization | 完全不返回 |
| 地址设备 | 详细住址 / IP / MAC / 设备 UUID / 定位 | 仅返回脱敏区域,不返回精准定位 |
| 财务业务 | CVV / 完整卡号 / 税号 / 交易流水 / 内部备注 | CVV 永不返回,卡号脱敏 |
| 系统内部 | DB 连接串 / 内网地址 / 服务账号 / 私钥 / 内部错误堆栈 | 完全不返回 |
Step 3:触发错误回显
- 构造异常输入(缺字段 / 类型错误 / 超长 / 非法字符)观察错误响应是否含 SQL 原文 / 堆栈 / 内部路径
- 触发认证异常 / 权限异常观察
WWW-Authenticate/ 错误消息是否泄露后端组件版本 - 触发 5xx 看是否落到默认错误页(含框架版本 / 调试栈)
Step 4:被动面扫描
robots.txt/sitemap.xml是否暴露内部路径或管理后台入口- HTML 注释 /
<script>内嵌 JSON / JSON-LD 是否含密钥 / 内部地址 / TODO - 响应头
Server/X-Powered-By/ 自定义头 / Set-Cookie 是否泄露版本或凭证 - 缓存控制:含 PII 的响应是否设置
Cache-Control: no-store/private
示例库
正例形态(代码层根因)
c.JSON(http.StatusOK, user)直接序列化整个 ORM 对象,含password_hash/secret_key字段(list-api-pii-plaintext-exposure)panic(err)或c.String(500, err.Error())把 SQL 错误原文 / 文件路径返回客户端(error-stacktrace-internal-ip-exposure)/.env//.git/config等静态文件可访问,含DB_PASSWORD=/AWS_SECRET_ACCESS_KEY=(static-env-file-credential-exposure)Set-Cookie: session=<base64 含明文 user_id+role>而非随机 ID(response-header-token-leak)- HTML 模板渲染时把整个 user 对象注入
<script>var user = {{.}}</script>,含 password_hash(html-template-object-pii-exposure)
窄化反例(必须避免)
以下是敏感信息泄露维度的典型窄化误判:
- "前端显示已脱敏
133****1234→ 安全" — 错。脱敏必须服务端做。检查 Network 面板里 API 实际返回的是133****1234还是明文13312341234。前端脱敏可被任何拿到接口的攻击者绕过。 - "已测了用户详情 API → 跳过订单 / 工单 / 客服 API" — 错。每种业务实体的 PII 暴露面独立,订单可能含收货人手机号、工单可能含投诉人身份证、客服记录可能含完整对话原文。
- "已在子系统 A 测了脱敏方案 → 假定 B/C/D 同样" — 错。跨子系统独立结账;不同团队 / 不同时期开发的子系统,脱敏组件可能完全不同。
- "返回看起来不是用户数据 → 跳过" — 错。后端配置、内网 IP、错误堆栈、SQL 原文、组件版本、私钥片段、内部账号同样敏感,sink 范围不止 PII。
- "已隐藏
password/detail字段 → 安全" — 错。可能在响应头、Set-Cookie、其他 endpoint variant(列表 vs 详情)、HTML 注释、JSON-LD、错误响应、日志查看接口等位置泄露。同实体不同入口需独立测。
反例义务(必须遵守)
为什么这里是「必须」:反例义务属于交付契约——"未发现敏感信息泄露"或"已脱敏"结论是覆盖完整性的产物声明,缺失反向验证清单会让下游误信"该维度全站安全"。
写"未发现敏感信息泄露"或"已脱敏"前,产物必须包含:
- 测过的敏感面入口完整清单(按 sink 语义枚举:业务列表/详情/导出 API + 错误响应 + 调试端点 + 元数据 + 静态备份残留 + robots/sitemap + 响应头/Set-Cookie + HTML 注释 + 日志查看;多子系统场景按子系统独立分组结账)
- 每个入口测过的敏感类别(PII / 凭证 / 财务 / 内部配置 / 系统标识)
- 每个入口的响应证据(脱敏前后字段对比、响应头快照、错误响应触发样本)
- 同实体跨变体(列表 vs 详情 vs 导出)的脱敏一致性证据
清单不完整 → 结论降级为 partial-coverage 并显式声明未覆盖范围。
特别警示:以"前端已脱敏"为由判 safe 的,结论降级为 partial-coverage-frontend-only——必须给出 API 真实响应体的明文 / 脱敏证据,否则不得宣称服务端已脱敏。
闭环验证要求(必须遵守)
通用闭环口径见同根目录 common/closure-verification.md(技能表 path 列同一抽取根下,需要时 read_file 读取)。核心:完整证据链才判 confirmed,中间信号最多 suspected。本漏洞特有要点:
- 仅凭"字段名疑似敏感 / 响应含占位 / 字段位置可疑"不得判
confirmed,需证明真实敏感数据被读取 - 不应获得该信息的接收方(匿名 / 普通用户 / 低权限角色)真实看到完整敏感明文,才构成完整证据链
- 若只是部分片段、样例值、占位符或不确定是否真实敏感数据,最多
suspected - 静态备份残留类(
.env/.git)需验证是否真含凭证内容,不仅靠"文件可访问"
判定标准
| 现象 | 判定 |
|---|---|
| 对外响应稳定返回完整敏感信息明文,且接收方范围不应获得这些真实数据(含响应头/Set-Cookie/错误堆栈/静态文件等任一 sink) | confirmed |
| 存在部分敏感字段疑似暴露,或暴露范围 / 真实性仍需进一步确认(如疑似密钥但未验证有效) | suspected |
| 敏感字段均按最小暴露原则脱敏,且响应头 / 错误响应 / 静态文件 / 元数据各 sink 都有覆盖证据 | not vulnerable |
| 只测了部分入口或部分敏感类别(如仅业务 API,未测错误响应/静态备份) | partial-coverage(不得宣称 safe) |
修复建议
- 建立统一脱敏组件,按字段类型强制掩码输出(手机号 / 身份证 / 卡号 / 邮箱 / IP 各有标准脱敏规则),脱敏必须服务端做
- 响应 DTO 与 ORM 模型分离,禁止直接序列化 ORM 对象;凭证类字段(password / token / secret)从 DTO 层移除
- 错误响应统一化:生产环境只返回通用错误码 + 唯一 trace_id,详细堆栈写入服务端日志
- 调试 / 诊断 / 元数据端点(
/debug//actuator//swagger)生产环境关闭或加强鉴权 - 静态资源服务禁止暴露
.env/.git/.DS_Store/ 备份文件,CI 阻断包含敏感文件的发布 - 响应头最小化:移除
Server/X-Powered-By;Set-Cookie 使用随机不可解码的 session ID - 缓存策略:含 PII 的响应强制
Cache-Control: no-store,CDN 配置排除敏感路径 - 按角色最小化返回字段(如普通用户列表不返回他人手机号),导出接口做二次审计与审批