Skip to content

MS_DotNetSerialize

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

.NET の Serialize

概要

  • 以下の様な様々なシリアライザも存在するが、
    ココでは、BinaryFormatter のバイナリシリアライズのみ取り扱う。

  • System.Runtime.Serialization 名前空間の BinaryFormatter クラスと共に使用する。

補足(最重要・現在の結論を先に): 本ページの主題である
BinaryFormatter は、現在の .NET では使ってはならない

バージョン 状態
.NET Framework 使用可能(ただし非推奨
.NET 5 Obsolete(警告)、ASP.NET Core では既定で無効
.NET 8 使用すると既定で実行時例外
.NET 9 以降 実装ごと削除PlatformNotSupportedException

理由は後述のセキュリティの通り、
デシリアライズ時に任意コード実行を許してしまう設計上の欠陥にある
(修正不能と判断され、廃止された)。

本ページは、既存資産の理解と移行判断のための記録として残す。
新規開発では System.Text.Json を、
バイナリが必要なら MessagePack / Protocol Buffers を選ぶこと。

詳細

使用例

BinaryFormatter のバイナリシリアライズを使用すると、以下のような処理を実装できる。

オブジェクト ⇔ バイト変換

  • オブジェクトをバイトに変換できる。
  • オブジェクトをバイトから復元できる。

オブジェクトのセーブ&ロードや転送

  • ファイルにセーブ&ロードできる。
  • ネットワーク経由で転送できる。

参考

補足(BinaryFormatter が便利だった理由): 他のシリアライザと違い、
「型を書かなくても、オブジェクト グラフを丸ごと保存できる」
という点が魅力だった。

// 属性を付けるだけで、参照関係も循環参照も含めて丸ごと保存
[Serializable]
class Node { public Node Next; public string Name; }
  • private フィールドも保存される
  • 循環参照を扱える(参照 ID で管理)
  • スキーマ定義(IDL)が不要

しかし、この「型情報をデータ側が持ち、それを信じて復元する
という仕組みそのものが、後述の脆弱性の正体でもあった。

移行先としては、

元の用途 移行先
設定・状態の保存 System.Text.Json
高速・小サイズが必要 MessagePackProtocol Buffers
プロセス間・キャッシュ MessagePack、DataContractSerializer
完全な複製(ディープ コピー) 手書き / ソース ジェネレーター(シリアライズを流用しない)

様々なシリアライザ

以下の様なシリアライザも存在する。

バイナリ

  • DataContractSerializer
  • MsgPackFormatter - MessagePack

XML

  • XmlSerializer
  • DataContractSerializer

JSON

  • 純正

    • DataContractJsonSerializer
    • JsonSerializer - System.Text.Json
  • その他

    • JsonSerializer - JSON.NET
    • JsonSerializer - JSON.NET BSON

JSONのparseを色々試してみた。

補足(現在の選択指針): 上記は網羅的だが、
現在の実務では選択肢はかなり絞られる

シリアライザ 現在の位置づけ
System.Text.Json 標準・第一候補。ソース生成で AOT 対応、高速
Json.NET (Newtonsoft) 既存資産・高度なカスタマイズが要る場合のみ
MessagePack for C# バイナリの第一候補。非常に高速
Protocol Buffers gRPC を使うなら必然
XmlSerializer SOAP / 既存の XML 連携
DataContractSerializer WCF 由来。新規では選ばない
DataContractJsonSerializer 事実上、役目を終えた
BinaryFormatter 使用禁止(.NET 9 で削除)

System.Text.Json は当初 Json.NET より機能が少なかったが、
現在は多態デシリアライズ、required、カスタム コンバーター等が揃い、
移行できない理由の方が少ない

相互運用性

相互運用性あり

相互運用性あり(ポインタだけ注意)

net, netcoreapp

以下の検証コードで検証した結果、基本的に相互運用性はありそう。

  • net ⇔ netcoreapp
    Windows 上であれば動作する。

  • netcoreapp(on Windows) ⇔ netcoreapp(on Linux)
    netcoreapp 同士であれば動作する。

  • net ⇔ netcoreapp(on Linux)
    netcoreapp(on Linux) → net の場合に限り動作しないケースがあった。
    (デシリアライズした Dictionary に IConvertible が無いとかそういう例外)。

補足(なぜ壊れやすいのか): BinaryFormatter の出力には
アセンブリ修飾された型名System.Private.CoreLib 等)が埋め込まれる。

.NET Framework   →  mscorlib, Version=4.0.0.0, ...
.NET Core / .NET →  System.Private.CoreLib, Version=...

このため、

  • アセンブリ名が違うと復元できない(型解決に失敗する)
  • 内部フィールドが変わると復元できない
    Dictionary<K,V> の内部実装は .NET Core で変更されている)

原文が遭遇した「Dictionary に IConvertible が無い」という例外は、
まさにこの内部実装差によるものと考えられる。

要点: BinaryFormatter の出力は
「同じランタイム・同じバージョンで読む」ことが前提であり、
永続化フォーマットとして使うべきではなかった
ファイルや DB に保存した BinaryFormatter の出力は、
ランタイム更新のたびに読めなくなるリスクを抱える。

パフォーマンス

  • 一般的に遅いとされる。

  • ゲーム業界では、MessagePackなどの、
    POCO シリアライズに特化した高速なシリアライザが開発されている。

補足(遅い理由): BinaryFormatter
すべての型をリフレクションで走査し(.NET の Reflection)、
型名文字列や参照 ID をデータに埋め込むため、
速度・サイズの両面で不利である。

一方、MessagePack for C# などは
事前にフォーマッターのコードを生成しておくことで
実行時のリフレクションを排し、桁違いの性能を出している。
「バイナリだから速い」のではなく、
コード生成しているから速い、という点が本質である。

セキュリティ

最近、問題視されてきている模様
(オープンなサービスなどで、不特定多数ユーザから
バイナリデータをインジェクションされるのが脅威)。

補足(脆弱性の仕組み/最新化): 原文の懸念は的中し、
これが BinaryFormatter 廃止の決定打となった
(CWE-502: Deserialization of Untrusted Data)。

なぜ危険か:

① データ側に「復元すべき型名」が書かれている
② BinaryFormatter は、それを信じて型を読み込み、インスタンス化する
③ 復元の過程で、その型のコンストラクタや
   IDeserializationCallback、finalizer などが動く
④ 攻撃者は「復元されるだけで危険な処理が走る型」を指定できる
   → 任意コード実行(RCE)

④ で使われる既存の型の連鎖を ガジェット チェーンと呼び、
.NET 標準ライブラリの中にも利用可能なものが多数見つかっている。

決定的なのは、「入力を検証すれば防げる」わけではない点である。
型を復元する前に中身を検査することが原理的にできないため、
Microsoft は修正ではなく廃止を選んだ。

同じ理由で、次のシリアライザも同様に危険とされ、
非推奨・削除の対象になっている。

  • BinaryFormatter
  • SoapFormatter
  • NetDataContractSerializer
  • LosFormatter / ObjectStateFormatter(ASP.NET の ViewState)

なお、JsonSerializer 系でも
型名をデータに含める設定(Json.NET の TypeNameHandling.All など)を
有効にすると同じ脆弱性が発生するため、
「JSON なら安全」ではない点に注意。

参考

パフォーマンス

セキュリティ

詳細はコチラ

実装

相互運用性検証

  • クロスプラットフォーム&高性能なシリアライザ

  • 双方とも、JSON のバイナリ版(CBOR)みたいなものだが、

  • 少々、思想が異なる。
    端的に言うと、

  • Google によって開発され、主に、gRPC で使用される。

  • Protocol Buffersは IDL を使用するので、

    • データの構造化に重きを置いていると言える。
    • クロスプラットフォーム対応が完全になり得る。

MessagePack

  • serialize/deserialize の速度とデータ長の圧縮をアドバンテージとしている。

  • IDL が無いので、クロスプラットフォームが保証されない部分がある模様。

  • なんとなく、CBOR的(CBORから参照されている)で、
    IETF 標準化が提案されたが、下位互換を重視するため
    標準化を拒否した等の、開発のポリシーが読み取れる。
    (開発者がビッグデータ界隈の出身である事を考えると解り易い)。

  • 参考

補足(.NET で使う場合の実際): 原文の整理は現在も有効だが、
.NET 実装としては次の点を補っておく。

MessagePack for C# protobuf(Google.Protobuf)
スキーマ 不要(属性を付けるだけ) .proto が必須
生成 ソース ジェネレーター(AOT 可) protoc でコード生成
速度 非常に速い(.NET 最速級) 速い
他言語連携 実装依存の部分あり 確実
版数互換 Key の番号付けで対応 フィールド番号で堅牢に対応

判断の目安:

  • .NET だけで完結し、速度が欲しい → MessagePack for C#
  • 他言語と連携する、gRPC を使う → Protocol Buffers
  • スキーマ進化を長期に管理したい → Protocol Buffers

Microsoft Learn


Tags: 移行, プログラミング, .NET開発

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally