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 chain の基礎です。信頼できない 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 が導入された場合、信頼できるデプロイ境界の外から入力を制御できるならセキュリティ上の問題として扱ってください。必要最小限の permitted_classes を指定した safe_load を優先し、互換性エラーを消すためだけに許可範囲を広げないでください。

FAQ

よくある質問

YAML.safe_load は完全に安全ですか?

Psych.safe_load はデシリアライズ可能なクラスを許可された集合(既定では基本的なスカラーとコレクション)に制限し、alias を拒否します。ただし、アプリケーション側の構造、サイズ、業務ルールは検証しません。必要なクラスだけ許可し、入力サイズを制限して返却データを検証してください。一部の YAML 実装や処理で alias 展開による DoS が発生しており、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():信頼できないデータのリスク