安全读取并脱敏请求体需先用http.maxbytesreader限制大小防oom,再对非get请求读取body并用bytes.newreader重设r.body,同步更新contentlength和header;结构化脱敏须基于预设敏感键列表动态递归处理json、query及header,仅字符串值替换为"*"。

中间件里怎么安全读取并脱敏请求体
直接 io.ReadAll(r.Body) 会导致后续 handler 拿不到数据,必须缓冲重写。关键不是“能不能读”,而是“怎么读完还能用”。
- 开头先用
http.MaxBytesReader包一层:r.Body = http.MaxBytesReader(nil, r.Body, 10<sup>6</sup>),防 OOM - 只对
r.Method != "GET"的请求尝试读 body;GET 请求 body 大多为空,但个别客户端会塞数据,需显式跳过 - 读完后用
bytes.NewReader重设r.Body,同时同步更新r.ContentLength和r.Header.Set("Content-Length", ...),否则gin.BindJSON()等会失败 - 若 Content-Type 是
application/x-www-form-urlencoded或multipart/form-data,优先调r.ParseForm()后取r.PostForm,避免手动解析出错
结构化日志里 password 字段怎么自动变 [REDACTED]
别指望日志库自动识别敏感字段——zap.Any("user", user) 会把明文 Password: "123" 原样打出来。真正起作用的,是你在构造日志前主动控制输出。
- 对结构体实现
zapcore.ObjectMarshaler接口,而不是靠反射或正则补救 - 必须用指针接收器:
func (u *User) MarshalLogObject(enc zapcore.ObjectEncoder) error,否则嵌套结构或 nil 字段无法处理 - 字段名要导出(首字母大写),非导出字段不会被访问;不要在
MarshalLogObject里调用可能 panic 的方法 - 如果不用 zap,
slog用户应实现slog.Valuer接口:func (p Password) LogValue() slog.Value,它比fmt.Stringer更底层、更可靠
HTTP header 和 query 参数怎么一并脱敏
只脱敏 body 不够,Authorization、Cookie、X-API-Key、URL 中的 ?token=xxx 同样高危。不能靠字符串替换,得按结构分别处理。
-
r.URL.Query()获取 query map 后遍历 key,匹配敏感词(如"token"、"auth")就替换 value;注意同步更新r.URL.RawQuery,否则日志里看到的是未脱敏原始串 - header 脱敏用
r.Header遍历,key 判定别只看全等,用strings.Contains(strings.ToLower(key), "auth")覆盖X-Auth-Token、authorization等变体 - 敏感 key 列表建议从配置加载,支持正则(如
^.*key$),避免硬编码;别用全局正则替换字符串,容易误杀hostname这类正常字段 - 脱敏值统一用固定掩码如
"***",不拼接原始长度,避免泄露位数信息
异步日志刷盘时脱敏逻辑放哪才安全
脱敏绝不能丢到后台 goroutine 里做——上下文早没了,*http.Request 可能已释放,正则编译或 JSON 解析还会拖慢批量 flush。
- 所有脱敏必须在主线程完成,生成终态
LogEntry(含已脱敏的 map 或结构体)后再发往logChan - 对
map[string]interface{}做浅拷贝,仅遍历白名单 key(如[]string{"phone", "id_card"})做替换,跳过未知字段 - 预编译正则:
pwdReg = regexp.MustCompile(<code>`(?i)password\s*[:=]\s*["']?[^"'\s]+`),别每次调用都regexp.Compile - 递归脱敏时,只处理字符串类型 value;数字、bool、nil 不动,避免破坏 JSON 结构或引发
json.Unmarshal失败
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











