FlawOpen

flawopen.com/Ruby YAML/Ruby YAML

对不可信输入使用 YAML.load 安全吗?

CWE-502: Deserialization of Untrusted Data参考页面
简要说明

检查应用实际加载的 Psych 版本:Ruby 3.1 随附的 Psych 4 将 YAML.load 改为默认安全反序列化。更早的 Psych 版本会构造任意对象;Psych 也可以脱离 Ruby 单独升级。YAML.unsafe_load 和 Marshal.load 会反序列化 Ruby 对象,绝不能接收不可信输入。

VULNERABLE
# Psych < 4: YAML.load used unsafe
# object construction by default.
YAML.load(params[:config])

# Every Psych version: explicitly unsafe.
YAML.unsafe_load(user_input)
Psych.unsafe_load(user_input)

# Marshal has no restricted safe-load API.
Marshal.load(cookie_value)
FIXED
# Explicit restricted types; keep this list minimal.
require "yaml"
require "date"

data = YAML.safe_load(
  input,
  permitted_classes: [Date],
  permitted_symbols: [],
  aliases: false
)

# For ordinary external data, prefer JSON and
# validate its shape, values, and input size.
data = JSON.parse(input)

YAML 为什么有风险

YAML 标签可由 Psych 映射为 Ruby 对象。不安全加载时,带标签的文档可以请求构造某个类,并为其序列化状态提供值;实际影响取决于可用类及其行为。这是反序列化 gadget 链的基础。对不可信 YAML 使用安全加载,并单独验证返回数据。Python pickle、PHP unserialize 和 Ruby Marshal.load 也有类似的一般风险。

Rails 等应用可能加载大量依赖,这会扩大不安全反序列化链可利用的类和行为范围。具体风险取决于部署情况,并非每个 Rails 应用或 YAML 文档都如此。

版本变化为何引发困惑

Psych 4 将 YAML.load 改为默认安全加载,并新增 unsafe_load 保留以前的对象构造行为。Ruby 3.1 随附 Psych 4,但应用也可选择其他 Psych gem 版本;应审查最终解析的依赖和运行时,而不只是 Ruby 版本。安全加载可能拒绝应用所需的类或 alias;应使用最小的显式允许列表迁移,而不是改用 unsafe_load。

升级时若引入 unsafe_load,只要输入可能被可信部署边界之外的来源影响,就应将该调用视为安全问题。优先使用 safe_load,并将 permitted_classes 限定为最少必需项;不要只为消除兼容性错误而放宽列表。

FAQ

常见问题

YAML.safe_load 完全安全吗?

Psych.safe_load 将可反序列化的类限制在允许集合中(默认是基本标量和集合类型),并默认拒绝 alias。但它不会校验应用层数据结构、大小或业务规则。只允许必需的类,限制输入大小,并验证返回数据。某些 YAML 实现和处理流程曾出现 alias 展开导致的拒绝服务;CVE-2024-35221 记录了 RubyGems 元数据解析中的一个案例。这并不表示每次 Psych.safe_load 调用都有漏洞。

如果我加载自己的配置文件呢?

包含在可信且具完整性保护的应用制品中的文件,其来源不同于用户上传或租户可控文件。即便是内部配置,也优先使用 safe_load,并仅允许需要的类。只有对固定且可证明可信的数据才使用 unsafe_load;绝不能用于用户或请求可影响的内容。

Marshal 是否有可接受的用途?

Ruby 中的另一项风险:Marshal.load

与 YAML 不同,Ruby 文档指出,Marshal.load 能重建进程中几乎任何已加载的类;处理不可信数据时可能导致远程代码执行。请将 Marshal 数据限制在可信边界内,绝不要反序列化用户提供或可能受攻击者影响的字节。

参考资料

另请参阅 不安全反序列化:风险与修复 | flawopen.com .NET BinaryFormatter:风险与迁移 Java ObjectInputStream 安全:降低反序列化风险 Newtonsoft.Json TypeNameHandling:安全配置? PHP unserialize() 与不可信数据 Python pickle.load():不可信数据的安全风险