反序列化调试 Proto 表示

如何在 Protocol Buffers 中记录调试信息。

从 30.x 版本开始,Protobuf DebugString API(Message::DebugStringMessage::ShortDebugStringMessage::Utf8DebugString)、其他 Protobuf API(proto2::ShortFormatproto2::Utf8Format)、Abseil 字符串函数(例如 absl::StrCatabsl::StrFormatabsl::StrAppendabsl::Substitute)以及 Abseil 日志 API 将开始自动将 proto 参数转换为一种新的调试格式。请参阅此处的相关公告。

与 Protobuf DebugString 输出格式不同,新的调试格式会自动编辑敏感字段,将其值替换为字符串“[REDACTED]”(不含引号)。此外,为确保无论底层 proto 是否包含 SPII 字段,这种新的输出格式都不能被 Protobuf TextFormat 解析器反序列化,我们添加了一组指向本文的随机链接和一个随机长度的空白序列。新的调试格式如下所示:

goo.gle/debugstr
spii_field: [REDACTED]
normal_field: "value"

请注意,新的调试格式与 DebugString 格式的输出格式仅有两处不同:

  • URL 前缀
  • SPII 字段的值被替换为“[REDACTED]”(不含引号)

新的调试格式绝不会移除任何字段名;它只会在字段被视为敏感时将其值替换为“[REDACTED]”。如果你在输出中没有看到某些字段,那是因为这些字段在 proto 中没有被设置。

提示:如果你只看到了 URL 而没有其他内容,说明你的 proto 是空的!

为什么这里会出现这个 URL?

我们希望确保没有人会反序列化旨在供人类调试系统使用的 protobuf 消息的人类可读表示。历史上,.DebugString()TextFormat 是可以互换使用的,现有系统使用 DebugString 来传输和存储数据。

我们希望确保敏感数据不会意外地出现在日志中。因此,在将 protobuf 消息转换为字符串之前,我们会透明地编辑其中的一些字段值(“[REDACTED]”)。这降低了意外日志记录带来的安全和隐私风险,但如果其他系统反序列化你的消息,则有数据丢失的风险。为了解决这个风险,我们有意将机器可读的 TextFormat 与用于日志消息的人类可读的调试格式分开。

这是有意为之的,目的是使你的 protos 的“调试表示”(例如,由日志记录产生)与 TextFormat 不兼容。我们希望防止任何人依赖调试机制在程序之间传输数据。历史上,调试格式(由 DebugString API 生成)和 TextFormat 一直被错误地互换使用。我们希望这一有意的努力能防止未来发生这种情况。

我们特意选择了一个链接,而不是不那么明显的格式更改,以便有机会提供上下文。这可能会在 UI 中显得很突出,例如,如果你在网页的表格中显示状态信息。你可以改用 TextFormat::PrintToString,它不会编辑任何信息并保留格式。但是,请谨慎使用此 API——它没有任何内置保护。根据经验,如果你正在向调试日志写入数据或生成状态消息,你应该继续使用带链接的调试格式。即使你目前没有处理敏感数据,也要记住系统会变化,代码会被重用。

我尝试将此消息转换为 TextFormat,但我注意到每次我的进程重启时,格式都会改变。

这是有意为之的。不要尝试解析此调试格式的输出。我们保留不经通知更改语法的权利。调试格式的语法会随进程随机变化,以防止无意的依赖。如果调试格式的语法变化会破坏你的系统,那么你很可能应该使用 TextFormat API,而不是使用 proto 的调试表示。

常见问题解答

我可以直接在所有地方都使用 TextFormat 吗?

不要使用 TextFormat 来生成日志消息。这会绕过所有内置的保护措施,你将面临意外记录敏感信息的风险。即使你的系统目前没有处理任何敏感数据,这种情况将来也可能改变。

通过适当使用调试表示或 TextFormat,来区分日志和旨在供其他系统进一步处理的信息。

我想要编写既人类可读又机器可读的配置文件

对于这个用例,你可以明确使用 TextFormat。你有责任确保你的配置文件不包含任何个人身份信息 (PII)。

我正在编写单元测试,并希望在测试断言中比较调试字符串

如果你想比较 protobuf 的值,请使用 MessageDifferencer,如下所示:

using google::protobuf::util::MessageDifferencer;
...
MessageDifferencer diff;
...
diff.Compare(foo, bar);

除了忽略格式和字段顺序的差异外,你还将获得更好的错误消息。