文章目录
- 一、日志脱敏到底要解决什么问题
- 二、第一步不是写正则,而是定义敏感数据分级
- 三、白名单优先,黑名单兜底
- 四、脱敏应该发生在哪一层
- 五、不同字段应该用不同脱敏算法
- 1. 遮罩:适合展示型字段
- 2. 带盐哈希:适合关联分析
- 3. 删除:适合凭据类字段
- 4. 摘要化:适合大段业务内容
- 六、结构化日志是脱敏落地的前提
- 七、不要让脱敏毁掉可观测性
- 八、规则中心要支持测试、灰度和回滚
- 九、测试和审计:脱敏必须可验证
- 1. 单元测试
- 2. 集成测试
- 3. 线上抽样扫描
- 4. 查询和导出审计
- 十、常见踩坑
- 1. 只处理业务字段,忘了请求头
- 2. 只处理成功日志,忘了异常日志
- 3. 混淆日志脱敏和数据库加密
- 4. 忽略第三方 SDK 的 verbose 日志
- 5. 脱敏后无法关联问题
- 十一、落地路线图
- 第一步:先止血
- 第二步:结构化改造
- 第三步:规则中心化
- 第四步:持续审计
- 十二、总结
线上排障离不开日志,但日志里一旦混入手机号、身份证号、邮箱、Token、Cookie、地址、用户输入原文、订单备注和第三方接口返回体,日志系统就会从“排障资产”变成“泄露放大器”。日志脱敏不是把几个字段替换成
***这么简单,而是要把敏感字段识别、脱敏策略、采集链路、权限审计和可观测性指标放到同一套工程体系里设计。
未脱敏日志的风险与脱敏目标
一、日志脱敏到底要解决什么问题
很多团队一开始打日志,目标只有一个:问题来了能查到。于是接口入参打一遍、出参打一遍、异常对象打一遍、HTTP Header 打一遍、第三方接口响应也打一遍。短期看排障很方便,长期看风险会快速累积。
典型风险有几类:
- 隐私泄露:手机号、邮箱、身份证号、地址、真实姓名、设备号等个人信息进入日志平台。
- 凭据落盘:
Authorization、Cookie、X-Api-Key、JWT、Session ID 被写入文件或日志平台。 - 业务数据外扩:客服对话、订单备注、合同编号、病历、简历、Prompt、上传文件摘要进入可被更多人检索的系统。
- 权限边界变宽:数据库权限可能很严格,但日志平台查询权限往往更宽,敏感数据绕过数据库治理流入了另一个入口。
- 事故追溯困难:泄露发生后,团队只能手工查日志语句,很难知道哪条规则漏了、哪次发布引入了风险。
所以日志脱敏的目标不是“让日志越少越好”,而是在三件事之间做平衡:
- 排障时能看清链路、错误阶段和关键上下文。
- 日志中不保留可直接还原用户隐私和系统凭据的原文。
- 规则、命中、查询和导出都可审计、可验证、可持续演进。
一句话概括:日志要能帮助你定位问题,但不能帮助别人还原用户和凭据。
敏感数据分级与识别思路
二、第一步不是写正则,而是定义敏感数据分级
很多人做日志脱敏,上来就写手机号、邮箱、身份证正则。但真实工程里,敏感信息不只来自固定格式,还来自业务语义。
建议先做一张敏感数据分级表:
| 类型 | 示例 | 推荐处理 |
|---|---|---|
| 认证凭据 | password、token、secret、cookie、authorization、验证码、私钥 | 删除或固定替换,禁止保留原文 |
| 个人身份信息 | 手机号、邮箱、身份证、姓名、地址、车牌、设备 ID | 遮罩或带盐哈希 |
| 金融交易信息 | 银行卡、支付账号、交易流水、退款原因 | 遮罩、哈希或仅保留业务状态 |
| 业务敏感内容 | 客服聊天、合同文本、简历、病历、工单备注、Prompt 原文 | 摘要化、长度统计、标签化,谨慎保留原文 |
| 系统内部信息 | 内网 IP、数据库连接串、桶路径、灰度规则、队列 topic | 按场景最小化输出,敏感配置不落盘 |
这里有一个关键点:字段名敏感和字段值敏感要同时考虑。
例如phone=13812345678既有敏感字段名,也有敏感字段值;而一段客服消息里可能没有phone字段名,却在自然语言里出现了手机号;某些业务字段叫content、remark、payload,它们格式不固定,但语义上非常敏感。
因此,日志脱敏至少要覆盖三类识别方式:
- 字段名识别:看到
password、token、secret、cookie等字段名时直接处理。 - 字段值识别:用正则或模型/规则识别手机号、邮箱、身份证、JWT、银行卡等模式。
- 业务上下文识别:结合接口、业务域、日志类型判断
content、remark、extra是否可能包含高敏内容。
三、白名单优先,黑名单兜底
日志脱敏最稳的设计不是“把所有敏感格式都识别出来”,而是先控制哪些字段允许进入日志。
白名单优先的意思是:默认只输出经过评估的安全字段,例如:
{"trace_id":"8f5c...","span_id":"api-order-create","endpoint":"/api/orders","method":"POST","status_code":502,"error_code":"PAYMENT_TIMEOUT","cost_ms":1280,"user_id_hash":"u_9a71f...","order_id_hash":"o_32c0a...","request_schema":"CreateOrderRequest(v3)","has_coupon":true,"item_count":3,"token_logged":false}这些字段能支持排障:知道是哪条链路、哪个接口、哪个错误码、耗时多少、是否同一个用户、请求结构是什么。但它不会暴露手机号、地址、Token 和完整请求体。
黑名单兜底用于拦截遗漏和临时日志,例如:
Authorization: Bearer ...Cookie: ...password=...token=...- URL 查询参数中的
access_token - 嵌套 JSON 里的
secretKey - 异常日志中的请求头和响应体
黑名单不能作为唯一方案,因为业务敏感内容不一定有固定格式;但它能挡住大量低级事故。正确姿势是:业务日志走白名单,兜底过滤靠黑名单。
四、脱敏应该发生在哪一层
日志从产生到被查询,通常会经过几层:业务代码、日志框架/SDK、采集 Agent、消息队列或日志网关、日志存储、查询平台和导出通道。
不同层适合解决不同问题。
| 层级 | 优点 | 局限 | 适合做什么 |
|---|---|---|---|
| 业务代码 | 最懂字段语义 | 依赖研发自觉,容易漏 | 最小化输出、语义脱敏 |
| 日志 SDK/框架 | 统一接入、覆盖面大 | 对业务语义理解有限 | 字段名规则、凭据拦截、结构化日志 |
| 采集 Agent | 适合历史系统兜底 | 原始日志可能已落盘 | 正则兜底、传输前过滤 |
| 日志网关 | 集中治理 | 延迟和成本增加 | 统一规则、命中统计、灰度发布 |
| 查询平台 | 权限控制灵活 | 不能阻止原文存储 | 分级展示、导出审批、审计 |
推荐组合是:
- 业务代码少打原文:不要打印完整请求体、响应体、Header。
- 日志 SDK 统一兜底:凭据类字段一律删除;常见 PII 做遮罩或哈希。
- 采集层兼容历史系统:对老服务、第三方组件日志做二次过滤。
- 查询层做权限和审计:普通人看脱敏结果,高权限查看必须审批并记录。
不要把希望全部寄托在查询层。因为一旦原文已经进入日志存储,备份、导出、同步、索引和缓存都可能成为新的风险点。
五、不同字段应该用不同脱敏算法
脱敏不是全部替换成***。不同字段的排障价值不同,处理方式也不同。
1. 遮罩:适合展示型字段
手机号、邮箱、身份证尾号等,有时需要让客服或运营确认“是不是这个用户”,可以保留部分信息。
13812345678 -> 138****5678 zhangsan@test.com -> z***n@test.com 110101199001011234 -> 110101********1234遮罩适合人工阅读,但不适合做稳定关联分析。
2. 带盐哈希:适合关联分析
如果排障时需要知道“是否同一个用户反复失败”,可以记录带盐哈希。
user_id=123456 -> user_id_hash=HMAC_SHA256(secret, "123456") phone=13812345678 -> phone_hash=HMAC_SHA256(secret, "13812345678")注意:不要用无盐 MD5/SHA1 直接处理手机号、邮箱这类低熵数据。手机号空间有限,很容易被撞库还原。更推荐 HMAC-SHA256,并把密钥交给 KMS 或配置中心管理。
3. 删除:适合凭据类字段
密码、验证码、私钥、Token、Cookie、Refresh Token 这类字段,通常没有排障保留原文的必要。
{"authorization":"[REDACTED]","cookie":"[REDACTED]","password":"[REDACTED]","token_logged":false}凭据类字段不要做可逆加密后留在日志里。真正需要调试鉴权问题时,可以记录“是否存在”“长度”“签名算法”“过期时间是否解析成功”“token_logged=false”,而不是记录 token 原文。
4. 摘要化:适合大段业务内容
客服对话、工单备注、Prompt、异常输入原文等大段文本,直接遮罩意义不大。更好的方式是保留结构摘要:
{"message_len":356,"message_lang":"zh-CN","contains_phone":true,"contains_email":false,"intent":"refund_request","raw_message_logged":false}这样既能定位“输入里有什么类型的信息”,又不把原文扩散到日志平台。
六、结构化日志是脱敏落地的前提
自由文本日志是脱敏的敌人。
下面这种日志很常见,但很难治理:
create order failed, request={"phone":"13812345678","address":"..."}, token=abc.xxx, response=...它把业务字段、凭据、响应体和错误信息混在一行字符串里。后续只能靠正则扫,误伤和漏报都很难避免。
更推荐结构化日志:
{"event":"order_create_failed","trace_id":"8f5c...","user_id_hash":"u_9a71f...","endpoint":"/api/orders","error_code":"PAYMENT_TIMEOUT","cost_ms":1280,"request_schema":"CreateOrderRequest(v3)","item_count":3,"has_address":true,"address_logged":false,"authorization_logged":false}结构化日志有几个好处:
- 字段语义清楚,脱敏规则可复用。
- 查询更稳定,不依赖模糊关键词。
- 容易做字段级权限和导出控制。
- 方便统计脱敏命中率、规则版本和漏报趋势。
建议团队统一日志字段规范,至少包括:trace_id、span_id、service、env、endpoint、operation、status_code、error_code、cost_ms、user_id_hash、resource_id_hash、safe_message、rule_version。
七、不要让脱敏毁掉可观测性
日志脱敏做得太粗,会让排障失去价值。比如所有用户都替换成[USER],所有参数都替换成[PARAMS],最后只能知道“有错误”,却不知道错误发生在哪个用户、哪个订单、哪个阶段、哪类请求。
可观测性场景下,建议保留这些安全但有价值的信息:
trace_id/span_id:串起一次请求。user_id_hash/order_id_hash:支持同一对象关联分析。error_code/status_code:定位失败类型。cost_ms/retry_count/fallback_used:定位性能和降级行为。request_schema/field_presence:知道参数结构是否符合预期。provider/upstream_status/rate_limit_remaining:定位上游依赖问题。token_logged=false/raw_body_logged=false:明确说明高危字段没有进入日志。
以外部支付接口失败为例,不推荐记录完整请求和响应:
{"event":"payment_call_failed","trace_id":"8f5c...","provider":"pay_gateway_a","status_code":504,"error_code":"UPSTREAM_TIMEOUT","order_id_hash":"o_32c0a...","amount_bucket":"100-500","request_schema":"PayRequest(v2)","has_signature":true,"signature_logged":false,"raw_response_logged":false,"cost_ms":3000,"retry_count":1,"fallback_used":true}这类日志能回答排障最关心的问题:哪个供应商、什么状态码、哪个错误码、是否重试、是否降级、是否同一订单重复失败,同时又不暴露签名、银行卡、手机号和完整响应体。
规则中心的测试、灰度与回滚
八、规则中心要支持测试、灰度和回滚
日志脱敏规则不是一次写完就结束。随着业务变化,接口字段、第三方 SDK、日志格式都会变化。
建议把规则中心设计成可运营的系统,而不是一堆散落在代码里的正则:
version:2026-06-15.1rules:-name:drop_authorizationtype:field_namepattern:"(?i)authorization|cookie|set-cookie|x-api-key"action:droprisk:high-name:mask_phonetype:value_regexpattern:"(?<!\\d)1[3-9]\\d{9}(?!\\d)"action:mask_phonerisk:medium-name:hash_user_idtype:field_namepattern:"user_id|uid"action:hmac_sha256risk:medium一个可落地的规则中心至少要有:
- 版本号:每条日志能知道使用了哪个规则版本。
- 测试样例:新增规则前用样例验证命中与误伤。
- 灰度发布:先在少量服务或测试环境开启。
- 命中统计:统计每条规则命中次数、服务分布、字段分布。
- 回滚能力:发现误伤时能快速恢复。
- 审计记录:谁改了规则、为什么改、审批人是谁。
不要让一个过度贪婪的正则直接上线全量服务。正则写错可能导致两种事故:要么漏掉敏感数据,要么把大量正常日志清空,排障时什么也看不到。
九、测试和审计:脱敏必须可验证
日志脱敏不能只靠代码评审。建议建立四层验证。
1. 单元测试
覆盖常见字段和格式:
- 手机号、邮箱、身份证、银行卡。
- JWT、Cookie、Authorization、API Key。
- 嵌套 JSON、数组、URL 查询参数。
- 异常堆栈和第三方 SDK 日志。
- 大段自然语言里的敏感信息。
2. 集成测试
在测试环境构造包含敏感字段的请求,检查最终进入日志平台的数据是否已经脱敏。重点不是看业务接口返回,而是看日志链路最终落库结果。
3. 线上抽样扫描
定期在日志平台上运行敏感模式扫描,例如:
- 是否出现疑似手机号、邮箱、身份证。
- 是否出现
Bearer、Set-Cookie、password=。 - 是否出现私钥、连接串、AK/SK 模式。
- 是否有服务突然大量命中高危规则。
扫描结果应该进入告警或工单,而不是停留在安全团队手工截图。
4. 查询和导出审计
日志平台本身也要治理:
- 谁查询了高敏索引。
- 谁使用了手机号、邮箱、token 等关键词搜索。
- 谁导出了日志。
- 谁查看了解密字段。
- 是否存在批量下载和异常查询行为。
当发现敏感日志泄露时,不要只删除几条日志。要追溯来源服务、日志语句、规则缺口、测试缺口和权限缺口。
日志脱敏常见踩坑
十、常见踩坑
1. 只处理业务字段,忘了请求头
很多泄露不是发生在phone字段,而是发生在Authorization、Cookie、X-Api-Key、Set-Cookie。请求头和响应头必须进入脱敏范围。
2. 只处理成功日志,忘了异常日志
异常处理里最容易直接打印完整对象:
log.error("call provider failed, request={}, response={}",request,response,e);失败时的信息往往更多,也更危险。异常日志应该只记录安全摘要。
3. 混淆日志脱敏和数据库加密
数据库字段加密不代表日志安全。业务代码把解密后的手机号、地址打印出来,数据库加密就绕过去了。
4. 忽略第三方 SDK 的 verbose 日志
HTTP 客户端、ORM、支付 SDK、AI SDK、消息队列客户端都可能打印请求和响应。上线前要检查默认日志级别,生产环境不要打开过细的 wire/debug 日志。
5. 脱敏后无法关联问题
把所有用户都替换成[USER]会让排障困难。更好的方式是保留user_id_hash、order_id_hash和trace_id,既不暴露原文,又能支持关联分析。
十一、落地路线图
如果团队现在还没有完整日志脱敏体系,可以按四步推进。
第一步:先止血
- 禁止打印完整请求头、完整请求体、完整响应体。
- 对
password、token、secret、authorization、cookie做全局删除。 - 关闭生产环境第三方 SDK verbose 日志。
- 对历史日志做一次敏感模式扫描,定位高风险服务。
第二步:结构化改造
- 统一
trace_id、span_id、service、endpoint、error_code、cost_ms等字段。 - 用
user_id_hash、resource_id_hash替代原始用户信息。 - 用
request_schema、field_presence、raw_body_logged=false替代完整请求体。
第三步:规则中心化
- 建设统一日志 SDK 或过滤组件。
- 沉淀字段名规则、字段值规则、业务上下文规则。
- 支持规则版本、测试样例、灰度发布、命中统计和回滚。
第四步:持续审计
- CI 中加入敏感日志扫描。
- 测试环境验收日志平台落库结果。
- 线上抽样扫描和告警。
- 查询、导出、解密都进入权限和审计流程。
十二、总结
日志脱敏不是安全团队给研发加的一层“麻烦规则”,而是可观测性体系的一部分。没有脱敏,日志越详细,风险越大;脱敏过度,日志又会失去排障价值。
一套好的日志脱敏机制应该满足四个条件:
- 默认不记录高敏原文。
- 关键问题仍然可关联、可定位、可复盘。
- 规则统一管理、可测试、可灰度、可回滚。
- 查询、导出、解密都有权限和审计。
最终要形成的不是一组正则,而是一条工程闭环:敏感字段识别 → 分级策略 → 源头脱敏 → 结构化日志 → 可观测性保留 → 测试审计 → 持续治理。