反序列化调试 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]”(不带引号)来编辑敏感字段。此外,为了确保 Protobuf TextFormat 解析器无法反序列化此新输出格式,无论底层 proto 是否包含 SPII 字段,我们都会添加一组指向本文的随机链接和随机长度的空白序列。新的调试格式如下所示:

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 与用于日志消息的人类可读调试格式分开。

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

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

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

这是有意为之的。请勿尝试解析此调试格式的输出。我们保留更改语法的权利,恕不另行通知。调试格式的语法会随进程随机更改,以防止无意中产生依赖。如果调试格式的语法更改会破坏您的系统,则您很可能应该使用 TextFormat API,而不是使用 proto 的调试表示。

常见问题解答

我可以到处都使用 TextFormat 吗?

请勿使用 TextFormat 生成日志消息。这将绕过所有内置保护,您可能会意外记录敏感信息。即使您的系统目前没有处理任何敏感数据,这种情况也可能在将来发生变化。

通过酌情使用调试表示或 TextFormat,将日志与用于其他系统进一步处理的信息区分开来。

我希望编写既可读又可机器读取的配置文件

对于此用例,您可以显式使用 TextFormat。您有责任确保您的配置文件不包含任何 PII。

我正在编写单元测试,并且希望在测试断言中比较 Debugstring

如果要比较 protobuf 值,请使用 MessageDifferencer,如下所示:

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

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