FlawOpen

flawopen.com/安全でないデシリアライズ/.NET BinaryFormatterの安全性

.NETのBinaryFormatterは安全ですか?

CWE-502: Deserialization of Untrusted Dataリファレンスガイド
短い答え:安全な設定はありません

MicrosoftはBinaryFormatterを安全ではなく、安全化できないと説明しています。入力を信頼できると思っていても、使用をやめるよう推奨しています。.NET 9ではAPI型は残りますが、組み込み実装は常にPlatformNotSupportedExceptionをスローします。旧ランタイムやサポート対象外の互換パッケージには元のリスクが残ります。

危険
// 信頼境界を越えるデータをデシリアライズしない。
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class LegacyReader
{
    public static object Deserialize(Stream input) =>
        new BinaryFormatter().Deserialize(input);
}

// 次の制限のないポリモーフィック形式も削除する:
SoapFormatter
NetDataContractSerializer
LosFormatter
ObjectStateFormatter  // ASP.NET ViewStateの内部で使用

// SerializationBinderでも安全にはならない。
移行
// データ形式はアプリケーションコードで宣言する。
// Nullable reference types make the DTO contract explicit.
#nullable enable
using System;
using System.IO;
using System.Text.Json;

public sealed record MyDto(int Id, string Name);

public static class DtoReader
{
    public static MyDto Parse(ReadOnlySpan<byte> json)
    {
        // 入力サイズを制限し、アプリ規則を検証する。
        if (json.Length > 65_536)
            throw new InvalidDataException();

        MyDto? value = JsonSerializer.Deserialize<MyDto>(json);
        if (value is null || value.Id <= 0 ||
            string.IsNullOrWhiteSpace(value.Name) ||
            value.Name.Length > 100)
            throw new InvalidDataException();

        return value;
    }
}

// 古いNRBFファイルはNrbfDecoderで調査する
// レコードを確認し、許可値だけをDTOへ移す。
// 任意のランタイム型を再生成しない。

BinaryFormatterが危険な理由

BinaryFormatter.Deserializeはストリームからオブジェクトグラフを再構築し、攻撃者が制御するデータに実行フローを左右させる可能性があります。Microsoftはサービス拒否、情報漏えい、リモートコード実行のリスクを警告しています。根本原因は制限のないポリモーフィックなデシリアライズです。シリアライズデータが処理される型やオブジェクト状態に影響できます。設定スイッチで未信頼バイトを安全に解析するAPIにはできません。

リスクは公開HTTPエンドポイントだけではありません。ファイル、キャッシュ、キュー、DBレコード、デスクトップクライアントのメッセージ、クラウド同期状態も後で攻撃者の影響を受ける可能性があります。バイトがシステムに入り移動する経路を追跡し、「自分たちが書いた」「内部データ」というラベルを恒久的な信頼の根拠にしないでください。

SerializationBinderだけでは不十分

独自のSerializationBinderは解決対象の型を制限できますが、Microsoftはbinderなどの設定ではサービス拒否を軽減できず、他の攻撃にも不十分な場合があると明記しています。binderは移行の代わりになりません。削除までの間も未信頼入力を受け入れないでください。

既存コードの利用箇所を探す

ソース、プロジェクト参照、ランタイム設定でBinaryFormatter、IFormatter、SoapFormatter、NetDataContractSerializer、LosFormatter、ObjectStateFormatterを検索します。セッションプロバイダー、キャッシュアダプター、キュー処理、ファイル入出力、旧ASP.NET Web Formsを確認します。ViewStateは通常MACで保護されています。保護を有効にし、machine keyを守ってください。そのMACも一般的なBinaryFormatterデシリアライズを安全にはしません。

形式とランタイムに応じて移行する

新しいデータには、互換性と形式に応じてSystem.Text.Json、DataContractSerializer、MessagePack、protobuf-netなど明示的な契約を選びます。直接置き換えられる製品はありません。古いNRBFファイルを保持する場合、.NET 9のNrbfDecoderは記録を読み取っても符号化された型をインスタンス化しません。構造を検証し、期待する値だけをDTOへ移します。WinForms/WPFのリソース、クリップボード、ドラッグ&ドロップは別途移行ガイドを確認してください。

よくある質問

自分で書き込んだデータだけを復元します。安全ですか?

Microsoftは、データが信頼できると思われる場合も移行を推奨しています。ストレージ、キャッシュ、ファイル、同期、下流サービスは信頼境界を越えることがあります。各データ源の書き込み可能な主体を調べ、前提に頼らずBinaryFormatterを廃止してください。

.NET 9へ移行するとどうなりますか?

.NET 9ではAPI型は残りますが、組み込み実装は以前の互換スイッチに関係なく常にPlatformNotSupportedExceptionをスローします。更新前に利用箇所を調査して移行してください。サポート対象外の互換パッケージは脆弱な実装を復元するため、対策にはなりません。

Json.NETは安全な代替ですか?

Json.NETの既定値はTypeNameHandling.Noneで、payloadの型メタデータから任意のCLR型を選びません。未信頼入力に対してNone以外を有効にしないでください。ポリモーフィズムが必要ならアプリ側の厳格なallowlistを使い、ライブラリの指針に従います。TypeNameHandlingは安全?も参照。

参考資料

関連項目 安全でないデシリアライズ:リスクと対策 | flawopen.com Java ObjectInputStreamの安全性とデシリアライズ対策 Newtonsoft.Json TypeNameHandling:安全な設定方法 PHP unserialize() と信頼できない入力 Python pickle.load():信頼できないデータのリスク Ruby YAML.load は安全?Psych のバージョンと safe_load