- 関数型インタフェース:
- これは関数型インタフェースなので、ラムダ式またはメソッド参照の代入先として使用できます。
警告: 信頼できないデータの直列化復元は、本質的に危険であり、回避する必要があります。 「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 classJVM全体の直列化復元フィルタ・ファクトリを設定および取得するユーティリティ・クラス、静的なJVM全体のフィルタ、またはパターン文字列からフィルタを作成するユーティリティ・クラス。static interfaceFilterInfoは、直列化復元されている現在のオブジェクトに関する情報とObjectInputStreamのステータスへのアクセスを提供します。static enumクラス、配列の長さ、参照数、深さ、およびストリーム・サイズのチェックのステータス。 -
メソッドのサマリー
修飾子と型メソッド説明static ObjectInputFilterallowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) クラスの述語がtrueの場合、Status.ALLOWEDを返すフィルタを返します。checkInput(ObjectInputFilter.FilterInfo filterInfo) クラス、配列の長さ、オブジェクト参照の数、深度、ストリーム・サイズ、その他利用可能なフィルタ情報を確認してください。static ObjectInputFiltermerge(ObjectInputFilter filter, ObjectInputFilter anotherFilter) フィルタと別のフィルタのステータスをマージするフィルタを返します。static ObjectInputFilterrejectFilter(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
-