
TiDB 日志脱敏机制全解析tidb_redact_log 系统变量与底层实现【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb导读数据库常存储身份证号、信用卡号等敏感信息这些信息可能以键值对等形式出现在错误消息里并随错误日志一起被打印、传输造成敏感数据泄露风险。TiDB 为此提供了tidb_redact_log系统变量开关可对日志与错误消息中的敏感内容进行统一脱敏。本文基于 pkg/errno/logredaction.md 的设计说明结合当前仓库的 系统变量定义 与 脱敏工具实现完整讲解该功能的三种工作模式、实战开启方法、底层原理与日志反脱敏处理方案。一、问题背景敏感信息为何会进入日志数据库系统中存储的数据往往包含敏感信息如客户身份证号、信用卡号等。在某些场景下这些敏感信息会以键值对等形式出现在错误消息中例如唯一键冲突错误会直接打印导致冲突的具体值mysql create table t (a int, unique key (a)); Query OK, 0 rows affected (0.00 sec) mysql insert into t values (1),(1); ERROR 1062 (23000): Duplicate entry 1 for key a上述错误中的1就是实际写入的数据值。当这类错误被捕获并记录到日志时连同执行的 SQL 语句一起敏感数据就被顺带写入了日志文件。TiDB 日志中可能出现的泄露点主要有两类错误消息本身如Duplicate entry 1 for key a直接包含数据值执行的 SQL 语句日志中记录的[sql...]字段会还原完整的 SQL 文本其中可能内嵌用户写入的敏感字面量。对于不希望敏感信息随日志扩散的用户TiDB 提供了可配置的开关来隐藏这些可能敏感的信息。二、核心开关tidb_redact_log 系统变量tidb_redact_log是 TiDB 的日志脱敏总开关。在 系统变量定义 中可以看到其完整元信息{Scope: vardef.ScopeGlobal | vardef.ScopeSession, Name: vardef.TiDBRedactLog, Value: vardef.DefTiDBRedactLog, Type: vardef.TypeEnum, PossibleValues: []string{vardef.Off, vardef.On, vardef.Marker}, InternalSessionVariable: true, SetGlobal: func(_ context.Context, s *SessionVars, val string) error { s.EnableRedactLog val // NOTE: switch of errors is a singleton, thus we can not set it for different sessions errors.RedactLogEnabled.Store(val) return nil }, SetSession: func(s *SessionVars, val string) error { s.EnableRedactLog val return nil }, },关键信息如下属性取值变量名tidb_redact_log作用域Global SessionScopeGlobal \| ScopeSession类型枚举TypeEnum可选值OFF、ON、MARKER内部变量是InternalSessionVariable: true对应地会话结构体SessionVars中也有EnableRedactLog字段见 pkg/sessionctx/variable/session.go#L1469-L1470注释明确说明其可能取值为OFF、ON、MARKER。值得注意的是SetGlobal回调中的一个重要设计设置全局变量时除了写入会话字段还会调用errors.RedactLogEnabled.Store(val)更新错误库pingcap/errors中的单例开关。源码注释特别指出errors 的开关是单例因此无法为不同 session 分别设置——也就是说真正控制错误消息脱敏生效的全局开关在SetGlobal时被同步更新而SetSession只影响会话内的 SQL 语句脱敏展示。三、三种模式OFF / ON / MARKER根据变量定义tidb_redact_log有三种枚举取值其行为由 pkg/util/redact/redact.go#L38-L63 中的String()函数统一实现// String will redact the input string according to mode. func String(mode string, input string) string { switch mode { case MARKER: b : strings.Builder{} b.Grow(len(input)) _, _ b.WriteRune(‹) for _, c : range input { if c ‹ || c › { _, _ b.WriteRune(c) _, _ b.WriteRune(c) } else { _, _ b.WriteRune(c) } } _, _ b.WriteRune(›) return b.String() case OFF: return input case ON: return default: // should never happen intest.Assert(false, invalid redact mode) return } }各模式语义如下OFF默认关闭原样返回输入字符串日志与错误消息保持明文行为不受影响ON完全脱敏返回空字符串敏感内容不进入日志。在错误消息场景下结合 errors 库的脱敏逻辑实际打印时会被替换为?占位符MARKER标记模式用特殊 Unicode 字符‹与›将敏感内容包裹起来日志中既能看到脱敏位置又能在事后借助工具恢复或彻底清除这些内容。若内容中本身出现‹或›实现会将其双写以区分于标记边界。此外 redact.go 还提供了配套的辅助函数NeedRedact()读取全局errors.RedactLogEnabled单例开关判断当前是否处于脱敏模式非Disable且非空即需要脱敏Value(arg)开启脱敏时统一返回?否则返回原始参数Key(key)开启脱敏时返回?否则返回 key 的大写十六进制编码WriteRedact(builder, v, redact)按模式写字符串——MARKER 写‹v›、Enable 写?、否则原样写入。四、实战验证开启前后对比1. 开启脱敏沿用原文档 logredaction.md 的示例执行mysql set global.tidb_redact_log1; Query OK, 0 rows affected (0.00 sec)说明在早期版本中该开关以布尔值形式出现1表示开启。当前仓库的变量定义为枚举类型可取OFF、ON、MARKER三种值因此实际使用时应写为set global.tidb_redact_log ON;或OFF/MARKER。2. 观察错误消息的变化开启脱敏后同样的重复键冲突mysql insert into t values (1),(1); ERROR 1062 (23000): Duplicate entry ? for key ?对比开启前Duplicate entry 1 for key a冲突的具体数据值1与索引名a都被替换为?占位符敏感信息不再暴露。3. 观察日志的变化对应的 TiDB 日志前后对比如下原文档示例# 开启 tidb_redact_log 之前 [2020/10/20 11:45:37.796 08:00] [INFO] [conn.go:800] [command dispatched failed] [conn5] [connInfoid:5, addr:127.0.0.1:57222 status:10, collation:utf8_general_ci, user:root] [commandQuery] [statusinTxn:0, autocommit:1] [sqlinsert into t values (1),(1)] [txn_modeOPTIMISTIC] [err[kv:1062]Duplicate entry 1 for key a] # 开启 tidb_redact_log 之后 [2020/10/20 11:45:49.539 08:00] [INFO] [conn.go:800] [command dispatched failed] [conn5] [connInfoid:5, addr:127.0.0.1:57222 status:10, collation:utf8_general_ci, user:root] [commandQuery] [statusinTxn:0, autocommit:1] [sqlinsert into t values ( ? ) , ( ? )] [txn_modeOPTIMISTIC] [err[kv:1062]Duplicate entry ? for key ?]可以看到脱敏同时覆盖了两个泄露面SQL 语句字段[sqlinsert into t values (1),(1)]变为[sqlinsert into t values ( ? ) , ( ? )]SQL 中的数值字面量被?替换错误消息字段[err[kv:1062]Duplicate entry 1 for key a]变为[err[kv:1062]Duplicate entry ? for key ?]。也就是说开启tidb_redact_log后敏感内容在错误消息和日志两个层面都被统一隐藏这也是该设计文档标题Log Redaction的核心目标。五、底层原理错误库单例开关与脱敏链路1. 全局开关如何生效从 系统变量定义 可以看到SetGlobal会把变量值Store到errors.RedactLogEnabled来自 pingcap/errors 库的原子单例。此后 TiDB 在打印错误、记录 SQL 时会依据这个全局状态决定是输出明文还是?占位符。例如 pkg/util/dbterror/terror_test.go#L40-L51 的测试就展示了如何向errors.RedactLogEnabled写入RedactLogEnable/RedactLogMarker来验证不同模式下的错误输出。2. 日志与错误的脱敏接入点从源码结构看脱敏逻辑被广泛接入日志输出路径连接层pkg/server/conn.go、pkg/server/conn_stmt.go在打印命令执行失败、语句信息时使用脱敏执行层pkg/executor/adapter.go在打印 SQL 与错误时调用脱敏表达式层pkg/expression/constant.go、pkg/expression/explain.go对常量与执行计划信息进行脱敏处理优化器与物理算子pkg/planner/core/operator/physicalop/下多个物理算子如physical_batch_point_get.go、physical_cte.go、physical_topn.go等在记录计划相关信息时使用RedactLogEnable/RedactLogMarker相关逻辑。这种变量开关 → 全局单例 → 各模块日志打印点的链路设计保证了开启后能对全链路日志输出生效而不必在每个业务模块中单独维护配置。3. 关键/值的脱敏策略对于日志中出现的键值对信息脱敏策略并不相同见 redact.go 的Key()与Value()Key键脱敏时统一替换为?不脱敏时输出大写十六进制编码Value值脱敏时统一替换为?。这一设计既保证了敏感数据不泄露也保留了日志中存在哪些字段的结构信息便于问题定位。六、MARKER 模式与日志反脱敏处理当设置为MARKER模式时日志中的敏感内容会被‹与›包裹。这种模式适合以下诉求既要保证日志发布到外部时敏感内容被明确标记出来又希望在内部排障时能精确恢复或彻底清除标记内容。pkg/util/redact/redact.go#L79-L182 提供了两个反脱敏入口DeRedactFile(remove bool, input string, output string)直接处理文件按行读取输入文件输出到指定文件output传-时输出到标准输出DeRedact(remove bool, input io.Reader, output io.Writer, sep string)面向任意 Reader/Writer 的底层实现按sep如换行符逐行处理。两个函数都逐行扫描‹...›标记remove true将标记内的敏感内容整体替换为一个?彻底清除敏感信息remove false去除‹›标记还原出原始明文供内部可信环境排障使用。同时对‹/›被内容本身包含的情况双写形式做了兼容解析避免误判标记边界。配套的单元测试位于 pkg/util/redact/redact_test.go。七、生态联动与其他脱敏机制的协同1. tidb_slow_log_masking 已被移除TiDB 历史上还有一个慢日志脱敏变量tidb_slow_log_masking。在 pkg/sessionctx/variable/removed.go#L43 中可以看到它已被移入removedSysVars映射并给出迁移提示tiDBSlowLogMasking: use tidb_redact_log instead,即旧有的慢日志脱敏开关统一由tidb_redact_log替代用户应改用后者完成日志脱敏。这也说明tidb_redact_log是当前仓库中日志脱敏的标准入口。2. 备份任务信息的凭据脱敏除了日志与错误消息redact.go 还定义了TaskInfoRedacted类型专门用于脱敏备份流任务backup.StreamBackupTaskInfo中的存储凭据S3AccessKey、SecretAccessKey、SseKmsKeyId替换为[REDACTED]GCSCredentialsBlob替换为[REDACTED]Azure BlobSharedKey、AccessSig、EncryptionKey替换为[REDACTED]。可以看到日志脱敏这一设计思想在仓库中已被扩展应用到更广泛的敏感信息输出场景而不仅仅局限于错误消息。八、使用建议与限制开启方式执行SET GLOBAL tidb_redact_log ON;使全部连接生效当前仓库该变量同时支持 Global 与 Session 作用域需要立即在当前会话生效可执行SET SESSION tidb_redact_log ON;。模式选择追求绝对安全、不需要事后还原使用ON需要保留哪里存在敏感信息的线索并支持事后反脱敏处理使用MARKER注意处理后的日志中会出现‹›字符排障需要完整信息时保持OFF。限制说明由于错误库开关是进程级单例无法为不同 session 维护不同的错误脱敏状态全局开关变更对所有连接生效。验证手段开启后可通过触发一次带数据值的错误如重复键冲突观察错误消息与日志中是否出现?占位符确认脱敏已生效。总结tidb_redact_log是 TiDB 日志脱敏的统一切入点它通过OFF/ON/MARKER三种枚举模式在错误消息、SQL 语句、慢日志等输出路径上统一隐藏敏感内容底层依托 errors 库的全局单例开关与 pkg/util/redact/redact.go 中的工具函数实现MARKER模式配合DeRedactFile工具可在事后对日志执行恢复或彻底清除。对于任何需要将日志交予外部或长期归档的场景建议优先评估开启tidb_redact_log从源头阻断敏感数据的扩散。【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考