- 関数型インタフェース:
- これは関数型インタフェースなので、ラムダ式またはメソッド参照の代入先として使用できます。
警告: 信頼できないデータの直列化復元は、本質的に危険であり、回避する必要があります。 「Java SEに対するセキュア・コーディングのガイドライン」の"直列化および直列化復元"セクションに従って、信頼できないデータを慎重に検証する必要があります。 「直列化フィルタリング」では、シリアル・フィルタの使いやすさに関するベスト・プラクティスについて説明します。
アプリケーション開発者は、直列化復元の脆弱性から保護するために、各コンポーネントまたはライブラリで直列化復元できるオブジェクトを明確に記述する必要があります。 コンテキストおよびユースケースごとに、開発者は適切なフィルタを作成して適用する必要があります。
直列化復元フィルタリング・ファクトリおよびフィルタ
直列化復元フィルタリングの部分は、フィルタ、コンポジット・フィルタおよびフィルタ・ファクトリです。 各フィルタでは、クラスとリソース制限のチェックが実行され、ステータスが否認済、許可済または未決定として決定されます。 フィルタは、他のフィルタから構成でき、結果をマージまたは結合できます。 フィルタ・ファクトリは、各ObjectInputStream
のフィルタの確立および更新を担当します。
単純なケースの場合、フィルタ・ファクトリを設定せずに、アプリケーション全体に対して静的なJVM全体のフィルタを設定できます。 JVM全体のフィルタは、コマンドラインでシステム・プロパティを使用するか、Config.setSerialFilterを呼び出して設定できます。 カスタム・フィルタ・ファクトリを指定する必要はありません。組込みフィルタ・ファクトリにデフォルト設定されます。 組込みフィルタ・ファクトリは、ObjectInputStreamごとに「静的JVM全体のフィルタ」を提供します。
たとえば、クラスの例を許可するフィルタでは、java.base
モジュールのクラスを許可し、他のすべてのクラスを拒否できます:
As a command line property:
% java -Djdk.serialFilter="example.*;java.base/*;!*" ...
Or programmatically:
var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*")
ObjectInputFilter.Config.setSerialFilter(filter);
複数の実行コンテキストを持つアプリケーションでは、アプリケーションごとにカスタム・フィルタを提供することで、個々のコンテキストを保護するための「フィルタ・ファクトリ」を提供できます。 ストリームが作成されると、フィルタ・ファクトリがコールされ、現在のスレッド・ローカル状態、コール元の階層、ライブラリ、モジュールおよびクラス・ローダーなど、使用可能な情報から実行コンテキストが識別されます。 その時点で、フィルタを作成または選択するためのフィルタ・ファクトリ・ポリシーは、コンテキストに基づいて特定のフィルタまたはフィルタの構成を選択できます。 JVM全体の直列化復元フィルタ・ファクトリにより、コンテキスト固有の直列化復元フィルタをすべてのObjectInputStream
に設定でき、ストリームから読み取られるすべてのオブジェクトをチェックできます。
フィルタ・ファクトリの起動
JVM全体のフィルタ・ファクトリは、各ObjectInputStream
が「構成され」、「ストリーム固有のフィルタが設定された」の場合に呼び出される関数です。 パラメータは現在のフィルタとリクエストされたフィルタで、ストリームに使用されるフィルタを返します。 「ObjectInputStreamコンストラクタ」から起動すると、最初のパラメータはnull
で、2番目のパラメータは「静的JVM全体のフィルタ」です。 ObjectInputStream.setObjectInputFilter
から呼び出されると、最初のパラメータはストリーム (コンストラクタで設定されたもの)で現在設定されているフィルタで、2番目のパラメータはObjectInputStream.setObjectInputFilter
に指定されたフィルタです。 現在および新しいフィルタはそれぞれnull
で、ファクトリはnull
を返します。 フィルタ・ファクトリの実装では、アプリケーション・スレッド・コンテキストまたはそのコール・スタックから抽出されたコンテキスト情報を自由に使用して、新しいフィルタを作成して組み合せることもできます。 2つのパラメータのみを使用するわけではありません。
アクティブな直列化復元フィルタ・ファクトリは、次のいずれかです:
- アプリケーション固有のフィルタ・ファクトリは、
ObjectInputFilter.Config.setSerialFilterFactory(BinaryOperator)
、システム・プロパティjdk.serialFilterFactory
またはセキュリティ・プロパティjdk.serialFilterFactory
によって設定されます。 - それ以外の場合、組み込みの直列化復元フィルタ・ファクトリは、「ObjectInputStreamコンストラクタ」から起動されたときに「静的JVM全体のフィルタ」を提供し、
ObjectInputStream.setObjectInputFilter(ObjectInputFilter)
から起動されたときに静的フィルタを置き換えます。 getSerialFilterFactoryを参照してください。
フィルタ
フィルタは、「パターン文字列」から、または「クラスの述語」からallowまたはrejectクラスに基づいて作成できます。フィルタcheckInput(FilterInfo)
メソッドは、「オブジェクトの読取り」中にゼロ回以上呼び出されます。 メソッドは、クラス、各配列の長さ、ストリームから読み取られるオブジェクト数、グラフの深さおよびストリームから読み取られたバイトの合計数を検証するためにコールされます。
複合フィルタは、他のフィルタの結果を結合またはチェックします。 merge(filter, anotherFilter)
フィルタは、2つのフィルタのステータス値を組み合せます。 rejectUndecidedClass(filter)
は、ステータスがUNDECIDED
の場合に、クラスのフィルタの結果をチェックします。 多くの場合、フィルタによるALLOWED
ではないクラスはREJECTED
にする必要があります。
直列化復元フィルタにより、引数を許可するか拒否するかを決定し、適切なステータスを返します: ALLOWED
またはREJECTED
。 フィルタがステータスを判別できない場合は、UNDECIDED
を返します。 フィルタは、特定のユースケースと予想される型用に設計する必要があります。 特定の用途向けに設計されたフィルタは、フィルタの範囲外のクラスに渡される場合があります。 フィルタの目的がクラスを拒否することである場合は、他の候補クラスでUNDECIDED
を照合し、レポートする候補クラスを拒否できます。 null
、arrayLength
= -1、深さ、参照数、およびストリーム・サイズに等しいクラスで呼び出され、値の1つまたは一部のみを反映するステータスを返します。 これにより、フィルタをレポートしている選択肢に特定でき、許可または否認済ステータスを強制せずに他のフィルタを使用できます。
フィルタ・モデルの例
単純なアプリケーションの場合、許可または拒否されたクラスをリストする単一の事前定義済フィルタで、予期しないクラスの直列化復元のリスクを管理できる可能性があります。複数のモジュールまたはライブラリから構成されるアプリケーションでは、アプリケーションの構造を使用して、アプリケーションの各コンテキストで許可または拒否するクラスを各ObjectInputStream
で識別できます。 各ストリームの構成時に直列化復元フィルタ・ファクトリが起動され、スレッドまたはプログラムを確認して適用するコンテキスト固有のフィルタを決定できます。 考えられる例:
- スレッド・ローカル状態は、ストリーム固有のフィルタで適用または構成するフィルタを保持できます。 フィルタは、アプリケーションまたはライブラリによって管理される仮想フィルタのスタックからプッシュおよびポップアップできます。
- フィルタ・ファクトリは、直列化復元メソッドの呼出し元を識別し、モジュールまたはライブラリ・コンテキストを使用してフィルタを選択するか、適切なコンテキスト固有のフィルタを構成できます。 メカニズムは、直列化されたクラスに対する制限付きアクセスまたは無制限のアクセスを持つ呼び出し元を識別し、それに応じてフィルタを選択できます。
スレッドのすべての直列化復元をフィルタする例
このクラスは、アプリケーション提供のフィルタ・ファクトリがフィルタを組み合せて、スレッドで実行されるすべての直列化復元操作をチェックする方法を示します。 スレッド固有フィルタを保持するスレッド・ローカル変数を定義し、静的JVM全体のフィルタおよびストリーム固有のフィルタを使用してそのフィルタを構成するフィルタ・ファクトリを作成します。doWithSerialFilter
メソッドは、スレッド固有のフィルタの設定を行い、アプリケーションRunnable
を起動します。
public static final class FilterInThread implements BinaryOperator<ObjectInputFilter> {
private final ThreadLocal<ObjectInputFilter> filterThreadLocal = new ThreadLocal<>();
// Construct a FilterInThread deserialization filter factory.
public FilterInThread() {}
// Returns a composite filter of the static JVM-wide filter, a thread-specific filter,
// and the stream-specific filter.
public ObjectInputFilter apply(ObjectInputFilter curr, ObjectInputFilter next) {
if (curr == null) {
// Called from the OIS constructor or perhaps OIS.setObjectInputFilter with no current filter
var filter = filterThreadLocal.get();
if (filter != null) {
// Wrap the filter to reject UNDECIDED results
filter = ObjectInputFilter.rejectUndecidedClass(filter);
}
if (next != null) {
// Merge the next filter with the thread filter, if any
// Initially this is the static JVM-wide filter passed from the OIS constructor
// Wrap the filter to reject UNDECIDED results
filter = ObjectInputFilter.merge(next, filter);
filter = ObjectInputFilter.rejectUndecidedClass(filter);
}
return filter;
} else {
// Called from OIS.setObjectInputFilter with a current filter and a stream-specific filter.
// The curr filter already incorporates the thread filter and static JVM-wide filter
// and rejection of undecided classes
// If there is a stream-specific filter wrap it and a filter to recheck for undecided
if (next != null) {
next = ObjectInputFilter.merge(next, curr);
next = ObjectInputFilter.rejectUndecidedClass(next);
return next;
}
return curr;
}
}
// Applies the filter to the thread and invokes the runnable.
public void doWithSerialFilter(ObjectInputFilter filter, Runnable runnable) {
var prevFilter = filterThreadLocal.get();
try {
filterThreadLocal.set(filter);
runnable.run();
} finally {
filterThreadLocal.set(prevFilter);
}
}
}
フィルタ・ファクトリの使用
FilterInThread
ユーティリティを使用するには、インスタンスを作成し、JVM全体のフィルタ・ファクトリとして構成します。 doWithSerialFilter
メソッドは、サンプル・アプリケーションとコア・クラスを許可するフィルタで呼び出されます:
// Create a FilterInThread filter factory and set
var filterInThread = new FilterInThread();
ObjectInputFilter.Config.setSerialFilterFactory(filterInThread);
// Create a filter to allow example.* classes and reject all others
var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*");
filterInThread.doWithSerialFilter(filter, () -> {
byte[] bytes = ...;
var o = deserializeObject(bytes);
});
特に指定のないかぎり、null
引数をこのインタフェースとそのネストされたクラスのメソッドに渡すと、NullPointerException
がスローされます。
- 導入されたバージョン:
- 9
- 関連項目:
-
ネストされたクラスのサマリー
修飾子と型インタフェース説明static final class
JVM全体の直列化復元フィルタ・ファクトリを設定および取得するユーティリティ・クラス、静的なJVM全体のフィルタ、またはパターン文字列からフィルタを作成するユーティリティ・クラス。static interface
FilterInfoは、直列化復元されている現在のオブジェクトに関する情報とObjectInputStream
のステータスへのアクセスを提供します。static enum
クラス、配列の長さ、参照数、深さ、およびストリーム・サイズのチェックのステータス。 -
メソッドのサマリー
修飾子と型メソッド説明static ObjectInputFilter
allowFilter
(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) クラスの述語がtrue
の場合、Status.ALLOWED
を返すフィルタを返します。checkInput
(ObjectInputFilter.FilterInfo filterInfo) クラス、配列の長さ、オブジェクト参照の数、深度、ストリーム・サイズ、その他利用可能なフィルタ情報を確認してください。static ObjectInputFilter
merge
(ObjectInputFilter filter, ObjectInputFilter anotherFilter) フィルタと別のフィルタのステータスをマージするフィルタを返します。static ObjectInputFilter
rejectFilter
(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) クラスの述語がtrue
の場合、Status.REJECTED
を返すフィルタを返します。static ObjectInputFilter
特定のフィルタを起動し、クラスについてUNDECIDED
をREJECTED
にマップするフィルタを特殊なケースとともに返し、それ以外の場合はステータスを返します。
-
メソッドの詳細
-
checkInput
ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) クラス、配列の長さ、オブジェクト参照の数、深度、ストリーム・サイズ、その他利用可能なフィルタ情報を確認してください。 このメソッドの実装は、直列化復元中に作成されるオブジェクト・グラフの内容をチェックします。 フィルタはStatus.ALLOWED
、Status.REJECTED
、またはStatus.UNDECIDED
を返します。filterInfo.serialClass()
がnon-null
の場合、チェックするクラスがあります。serialClass()
がnull
の場合、クラスがなく、この情報には、直列化復元されるグラフの深さ、参照の数およびストリーム読取りサイズに関連するメトリックのみが含まれます。- APIのノート:
checkInput
を実装する各フィルタは、ObjectInputFilter.Status
の値のいずれかを返します。null
を返すと、NullPointerException
やその他の予期しない動作が発生する可能性があります。- パラメータ:
filterInfo
- 直列化復元されている現在のオブジェクトに関する情報と、ObjectInputStream
のステータスを返します- 戻り値:
Status.ALLOWED
が拒否された場合はStatus.REJECTED
、未確定の場合はStatus.UNDECIDED
。
-
allowFilter
static ObjectInputFilter allowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) クラスの述語がtrue
の場合、Status.ALLOWED
を返すフィルタを返します。 フィルタは、non-null
クラスの述語に基づいてALLOWED
またはotherStatus
を返し、クラスがnull
の場合はUNDECIDED
を返します。フィルタ
checkInput(info)
メソッドが起動されると、述語がinfo.serialClass()
に適用されます。戻りステータスは次のとおりです:たとえば、プラットフォームまたはブートストラップ・クラス・ローダーからロードされたクラスを許可するフィルタを作成します。
ObjectInputFilter f = allowFilter(cl -> cl.getClassLoader() == ClassLoader.getPlatformClassLoader() || cl.getClassLoader() == null, Status.UNDECIDED);
- パラメータ:
predicate
-nullでないクラスをテストする条件otherStatus
- 述語がfalse
の場合に使用するステータス- 戻り値:
- クラスの述語が
true
の場合、ALLOWED
を返すフィルタ - 導入されたバージョン:
- 17
-
rejectFilter
static ObjectInputFilter rejectFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) クラスの述語がtrue
の場合、Status.REJECTED
を返すフィルタを返します。 フィルタは、non-null
クラスの述語に基づいてREJECTED
またはotherStatus
を返し、クラスがnull
の場合はUNDECIDED
を返します。 フィルタcheckInput(info)
メソッドが起動されると、述語がserialClass()
に適用されます。戻りステータスは次のとおりです:たとえば、アプリケーション・クラス・ローダーからロードしたクラスを拒否するフィルタを作成します。
ObjectInputFilter f = rejectFilter(cl -> cl.getClassLoader() == ClassLoader.ClassLoader.getSystemClassLoader(), Status.UNDECIDED);
- パラメータ:
predicate
-nullでないクラスをテストする条件otherStatus
- 述語がfalse
の場合に使用するステータス- 戻り値:
- クラスの述語が
true
の場合、REJECTED
を返すフィルタを返します - 導入されたバージョン:
- 17
-
merge
static ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter) フィルタと別のフィルタのステータスをマージするフィルタを返します。another
フィルタがnull
の場合、filter
が返されます。 それ以外の場合は、non-null
フィルタのペアをマージするためにfilter
が返されます。 返されるフィルタは、次のようにcheckInput(FilterInfo)
メソッドを実装します:FilterInfo
でfilter
を呼び出してstatus
を取得status
がREJECTED
の場合、REJECTED
を返しますanotherFilter
を呼び出してotherStatus
を取得otherStatus
がREJECTED
の場合、REJECTED
を返しますstatus
またはotherStatus
のいずれかがALLOWED
の場合は、ALLOWED
を返します。- それ以外の場合は、
UNDECIDED
を返します
- パラメータ:
filter
- フィルタanotherFilter
- フィルタとマージするフィルタ(null
)- 戻り値:
- フィルタと別のフィルタのステータスをマージする
ObjectInputFilter
- 導入されたバージョン:
- 17
-
rejectUndecidedClass
static ObjectInputFilter rejectUndecidedClass(ObjectInputFilter filter) 特定のフィルタを起動し、クラスについてUNDECIDED
をREJECTED
にマップするフィルタを特殊なケースとともに返し、それ以外の場合はステータスを返します。 クラスがプリミティブ・クラスではなく配列の場合、返されるステータスはREJECTED
です。 クラスがプリミティブ・クラスまたは配列クラスの追加チェックが実行される場合は、次のリストを参照してください。フィルタが
UNDECIDED
を返す場合、オブジェクトの直列化復元はクラスを受け入れます。 許可または拒否されていないクラスに対して決定されていない結果を拒否するフィルタを追加すると、クラスがフィルタを通過しないようにすることができます。- 実装要件:
- 返されるフィルタは、次のように
checkInput(FilterInfo)
メソッドを実装します:FilterInfo
でフィルタを起動して、そのstatus
を取得- ステータスが
REJECTED
またはALLOWED
の場合、status
を返します filterInfo.getSerialClass() serialClass
がnull
の場合、UNDECIDED
を返します- クラスが「配列」でない場合、
REJECTED
を返します serialClass
が「配列」の場合、ベース・コンポーネント・タイプを決定- ベース・コンポーネント・タイプが「プリミティブ・クラス」の場合は、
UNDECIDED
を返します base component type
でフィルタを起動して、そのcomponent status
を取得- コンポーネント・ステータスが
ALLOWED
の場合は、ALLOWED
を返します - それ以外の場合は、
REJECTED
を返します。
- パラメータ:
filter
- フィルタ- 戻り値:
- クラスの
ObjectInputFilter.Status.UNDECIDED
ステータスをObjectInputFilter.Status.REJECTED
にマップするObjectInputFilter
で、それ以外の場合はフィルタ・ステータスを返します - 導入されたバージョン:
- 17
-