要約

  • XML では、プロトコルやスキーマが別の扱いを定めない限り、改行や字下げも文字情報になり得る。整形は無害な表示変更とは限らない。
  • 受信オクテット、パーサー設定、infoset、検証、正規化、署名範囲、認可、状態遷移、利用者の結果は別々に記録する必要がある。

運用担当者は、届いた XML を読みやすく整形した。要素の間に改行を入れ、字下げを揃えただけだった。ところが、相手側のアプリケーションは値が変わったと判断した。

人間には余白でも、XML 処理系には文字情報である。要素だけを含む構造なのか、混合内容なのか、どの空白を保存するのかをプロトコルが定めていなければ、「見た目だけの変更」という共通理解は成立しない。

2003 年 1 月に Best Current Practice 70 として公開された RFC 3470 は、XML をすべてのプロトコルに推奨した文書ではない。XML を選んだ設計者に、構文処理の先に残る決定を明記させる文書である。

整形式は処理開始の条件にすぎない

開始タグと終了タグ、文字、属性などが XML の規則を満たせば、文書は整形式である。RFC 3470 は、整形式でない文書を部分的に解釈してはならないとする。再送を求めるか処理を中止するかはプロトコル次第だが、読めた断片だけで状態を進めるべきではない。

整形式の文書から、パーサーは XML Information Set を作る。その過程で文字エンコーディング、行末、実体、属性値などが処理される。アプリケーションが見る木は、受信したバイト列そのものではない。

したがって、最初に残すべきものは受信オクテットのハッシュ、媒体型、時刻、転送経路、エンコーディングの根拠である。次にパーサーの製品・版・設定・外部アクセス方針と整形式判定を記録する。「XML を受理した」という一行では再現できない。

XML 宣言は常に受理できるべきであり、UTF-8 と UTF-16 以外を許すなら必須になる。文字に変換する前の座標を失えば、その後の比較は同じ対象を見ている保証がない。

空白は表示とデータの境界にいる

XML は、空白文字を原則として文字情報として渡す。スキーマやプロトコルが特別な規則を定めれば扱いを限定できるが、単なる慣習では不十分である。

要素の値がテキストなら、字下げや改行が値に混ざる可能性がある。混合内容では、文章中の空白を削除すると意味や読み方まで変わる。署名対象であれば、整形前後で入力が変わることもある。

一方、属性の並び順には XML 上の意味がない。属性値は規則に従って正規化されることもある。送信側のシリアライザーが偶然出した順番に意味を持たせる実装は、私的なプロトコルを作っている。

空白、要素順、混合内容、属性と要素の使い分け、再シリアライズ時の保存範囲を仕様に書く必要がある。画面で同じに見えることも、木が同じに見えることも、バイト同一性の証明ではない。

正規化は比較対象を指定して初めて意味を持つ

RFC 3076 の Canonical XML は、ノード集合を共通の規則で直列化する。引用符や名前空間宣言など一部の表現差を吸収できる。しかし、どのノード集合を選んだかは別の決定である。

RFC 3275 の XML Signature は参照と変換を使う。有効な署名は、指定された参照と変換後のデータについてだけ語る。アプリケーションが未署名の隣接要素や、外部スキーマから補われた値を使えば、その決定は署名の外側にある。

記録には参照先、変換列、正規化手法、鍵、認証された主体、アプリケーションが実際に読んだノードが必要だ。署名成功は権限付与でも、処理完了でもない。

独自の XML 部分集合で正規化を代用すると、正当な別表現を出す相手を排除する。RFC 3470 が避けようとしたのは、実装上の都合が相互運用条件に化けることだった。

名前空間は意味への索引であり権限証明ではない

名前空間 URI は語彙を区別する識別子で、必ず取得する文書ではない。URI に組織のドメインが含まれても、送信者がその組織を代表する証拠にはならない。

既定の名前空間が適用されるのは接頭辞のない要素であり、接頭辞のない属性ではない。見た目の入れ子だけで同じ語彙だと解釈すれば、別名のフィールドに権限を与えかねない。

未知の拡張を無視、保存、転送、拒否するのか、理解必須として失敗するのかもプロトコルが決める。整形式判定はこの政策を持たない。処理命令やコメントに規範的な拡張を隠すべきでない理由も同じである。

ログには接頭辞ではなく展開名と選ばれた処理規則を残す。接頭辞は変えられるが、語彙の識別は展開名にある。

検証は構造の領収書である

DTD、XML Schema などは異なる制約を表現する。RFC 3470 は、いずれの形式言語でもプロトコルの全条件を表せず、意味規則が文章とアプリケーションに残ると指摘する。

検証結果にはスキーマやプロファイルの識別子、版、取得元、既定値の挿入有無が必要だ。検証器が既定属性を木に加えると、パケット解析器とアプリケーションで見える値が違う。通信路に存在しなかった値を後から生成した処理を明示しなければならない。

型に適合する値でも、事実とは限らず、期限内とも、現在の状態で許可されているとも限らない。構造検証から認可を推論してはならない。

外部参照はパーサーに通信権限を与える

外部実体、遠隔スキーマ、相対 URI は、文書の外に意味を依存させる。しかも取得要求はパーサーのネットワーク位置から出る。外部の送信者が直接届かない内部資源へ間接的に触れたり、環境情報を漏らしたり、資源を消費させたりできる。

RFC 3470 はプロトコルで実体宣言を避け、信頼できない XML の外部参照による任意アクセスを許さないよう求める。五つの定義済み実体と数値文字参照は遠隔取得を伴わないため、通常の構文として扱える。

xml:base と転送層や包含文書の基底 URI の優先順位も必要だ。同じ相対参照を別の場所へ解決する実装は、同じ文書を処理していない。

再現には、外部アクセスを無効にした証明、固定カタログ、または取得した全オブジェクトのハッシュが要る。後日同じ URL を開くことは、当時の判断の再生ではない。

文法が安全でも実装は無制限ではない

XML には、機密性、完全性、認証、認可が組み込まれていない。パーサーは攻撃者が作る深さ、展開、長さ、参照を処理するコードであり、資源枯渇やメモリ障害の対象になる。

サイズ、深さ、実体展開、処理時間、メモリ、外部接続の上限を設定し、失敗時の完全な取り消しを記録する。文法上正しい入力でも運用上は拒否すべき負荷になり得る。

RFC 8996 は後に TLS 1.0 と 1.1 を非推奨として RFC 3470 の輸送セキュリティ背景を更新した。新しい TLS でも、空白の意味、署名範囲、業務認可は決まらない。

整形事故を再現する十四の記録

受信オクテットとエンコーディング、パーサーと設定、整形式判定、全体失敗、実体・基底 URI・外部資源、infoset、スキーマと検証、名前空間と拡張、正規化、署名参照、認証主体、意味検査、認可、状態遷移、最終結果を分ける。

相互運用性を主張するなら、外部アクセスを切り、別の準拠実装で同じ捕捉データを処理する。整形前後の両方を保存し、どの段階で差が生まれたかを示す。

証拠の境界

本稿は、特定の製品、パーサー、事故を扱わない。すべての空白が重要だとも、スキーマや署名が無用だとも主張しない。それぞれの証明範囲を分ける。

既存の YANG/XML 記事が扱う有効 YANG ツリー、既定値、スキーマ改訂、マウント、データストア、ネットワーク効果とも重複しない。ここでの対象は一般の XML プロトコルにおける表示と意味の境界である。

Heng Lu の最小初期仕様と実行コード優先の原則は、明示した編集上の視角である。必要な相互運用規則だけを共有し、実行時の記録で主張を制約する。導入実態の計測ではない。

結論は単純だ。空白を無意味にしたいなら、そう読める仕様と検証結果が必要である。「きれいにしただけ」は証拠にならない。

Sources