日志里的敏感信息:脱敏的边界与方法
竹影三尺
4 分钟阅读
日志是排障的生命线,但把原始请求体原样落盘,常常等于把用户的密码、令牌、身份证号免费存了一份。脱敏的边界只有一条:凡是不服务于排障、却可用于冒充或识别个体的字段,都该在进入存储前被抹掉或替换。
哪些是必须脱敏的字段
经验上高频出问题的几类:
- 凭据类:密码、token、API key、session cookie、Authorization 头。
- 个人标识:手机号、身份证、邮箱、银行卡号、精确住址。
- 受监管数据:健康信息、行踪、生物特征。
一个典型反面教材是登录接口把 body 完整记进 access log:{"password":"plain123"}。这行日志一旦被任意一个能读日志的人看到,账户就裸奔了。
脱敏的几种手段
最简单可靠的是"不记录"。在网关或框架的日志中间件里,显式剔除敏感键再序列化:
SENSITIVE_KEYS = {"password", "token", "authorization", "cookie", "id_card"}
def scrub(record: dict) -> dict:
out = {}
for k, v in record.items():
if k.lower() in SENSITIVE_KEYS:
out[k] = "***"
elif isinstance(v, dict):
out[k] = scrub(v)
else:
out[k] = v
return out
需要保留可辨识性时做部分掩码,而非整段删除:
def mask_phone(p: str) -> str:
return p[:3] + "****" + p[-4:] if len(p) == 11 else "****"
# 138****8888 仍能在工单里核对归属,又暴露不出完整号码
边界条件:掩码长度要够。只保留前三位后四位在 11 位手机号上还剩约 10^4 组合,单看一条日志无害;但若日志里同时带了注册时间、地区,组合去匿名化风险会上升。高敏感场景宁可只留哈希前缀用于关联。
脱敏该放在哪一层
越早越好。最稳的是在产生日志的进程内脱敏,避免敏感值在传输途中(比如先发到集中式日志收集的 agent)才被处理。若必须在后端统一脱敏,要确保传输通道本身加密,否则只是把风险从日志存储挪到了网络链路。
踩坑:脱敏只做了"请求日志",却忘了响应体、异常堆栈、SQL 慢查询日志里同样会带参数。一次未捕获的异常,可能把整条带密码的插入语句打进 stderr。
留存与权限是另一半
脱敏解决"记什么",留存策略解决"记多久、谁能看"。即便脱敏后的日志,聚合后也可能泄露行为画像。建议:按数据分级设不同保留期,原始级短、统计级长;日志系统的读权限走最小授权,和分析数据库分离。脱敏不是终点,访问可控才是闭环。
验证脱敏真的生效
脱敏代码最容易"看起来写了却没接上"。把校验写进测试:构造含敏感字段的日志事件,断言输出里不再出现原值。经验上,靠人工 code review 盯脱敏,遗漏率远高于一条自动化断言。
脱敏与可观测性的平衡
脱敏过头也会坏事:把订单号、用户 ID 全抹了,出了故障谁的问题都对不上。更好的做法是保留可关联但不含敏感的字段——用稳定哈希前缀代替原始 ID,既能跨日志串联同一用户,又不暴露真实身份。对必须保留用于排障的少量标识,明确写进数据分级文档,让审计知道为什么留、留多久。
输出环节的二次检查
日志除了进存储,常被同步到监控、告警、第三方分析平台。每一处下游都要当作独立出口复核:你脱敏的是原始日志,下游管道若另存了未脱敏副本,等于白做。在日志出口统一做脱敏网关,比在十个业务里各写一遍更不容易漏。
浏览 0
点赞 0
评论 0