XMLDecoderによる安全でない逆シリアル化

XMLDecoderによる信頼できない入力の逆シリアル化

説明

java.beans.XMLDecoder はXMLを単なるデータとして解析するのではなく、オブジェクトの生成やメソッド呼び出しを復元します。信頼できない入力を解釈すると、攻撃者がアプリケーションの権限で意図しない操作を実行できるおそれがあります。問題になるのはデコーダーのコンストラクター呼び出しだけでなく、文書の解釈中に実行される処理です。

想定される影響

  • コードの実行: 入力が選択したコンストラクターやメソッドにより、プロセスの権限で操作されるおそれがあります。
  • 機密情報へのアクセス: アプリケーションが利用できるデータやシステムリソースが露出する可能性があります。
  • データの変更とサービス障害: オブジェクトの状態を変更されたり、処理リソースを消費されたりするおそれがあります。

対処方法

  1. 信頼できないデータに XMLDecoder を使用しないでください。必要なフィールドだけを持つ明示的なモデルとJSONなどのデータ形式に、プロトコルを変更してください。
  2. カスタム ClassLoader やクラスの許可リストだけで、XMLDecoder を安全にできると考えないでください。
  3. フィールド、型、サイズを検証し、任意のクラス名やポリモーフィックな型情報を受け入れないでください。XMLが必要な場合はオブジェクト復元ではなくデータパーサーを使い、DTDと外部エンティティも制限してください。

例

変更前

java
import java.beans.XMLDecoder;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

public class InsecureXMLDecoder {
    public static Object decode(String untrustedXml) {
        try (XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream(
                untrustedXml.getBytes(StandardCharsets.UTF_8)))) {
            return decoder.readObject();
        }
    }
}

入力がオブジェクトや実行する操作を指定できます。オブジェクトを生成することと、実際にプロセスを起動することは別の操作です。

変更後

java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class MessageReader {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static String readMessage(String json) throws IOException {
        if (json == null || json.length() > 4096) {
            throw new IllegalArgumentException("Invalid message size");
        }
        JsonNode data = MAPPER.readTree(json);
        if (data == null || !data.isObject() || data.size() != 1
                || !data.path("message").isTextual()) {
            throw new IllegalArgumentException("Expected a message string");
        }
        return data.get("message").textValue();
    }
}

JacksonのJSONツリーから、message という文字列だけを受け取ります。任意の型の復元は有効にしません。既存のXMLオブジェクト形式とは互換性がないため、呼び出し側もJSONを送るように変更してください。リクエスト本文のサイズ制限とアクセス権限の確認は、呼び出し境界で別途行ってください。

参考資料