Интерфейс ObjectInputFilter
- Функциональный интерфейс:
- Это функциональный интерфейс, и поэтому он может использоваться в качестве целевого назначения лямбда-выражения или ссылки на метод.
@FunctionalInterface public interface ObjectInputFilter
Предупреждение: Десериализация недоверенных данных по своей природе опасна и должна быть избегаема. Недоверенные данные должны быть тщательно проверены в соответствии с разделом "Сериализация и десериализация" Руководства по защищенному программированию для Java SE. Фильтрация сериализации описывает лучшие практики для защищенного использования фильтров сериализации.
Для защиты от уязвимостей при десериализации разработчики приложений нуждаются в четком описании объектов, которые могут быть десериализованы каждым компонентом или библиотекой. Для каждого контекста и случая использования разработчики должны создавать и применять соответствующий фильтр.
Фабрика и фильтры для фильтрации десериализации
Части фильтрации десериализации — это фильтры, составные фильтры и фабрика фильтров. Каждый фильтр выполняет проверки классов и лимитов ресурсов, чтобы определить статус как отклоненный, разрешенный или неопределенный. Фильтры могут быть составлены из других фильтров и объединять или комбинировать их результаты. Фабрика фильтров отвечает за установление и обновление фильтра для каждогоObjectInputStream. В простых случаях можно установить статический фильтр JVM для всего приложения, не устанавливая фабрику фильтров. Статический фильтр JVM можно установить либо с помощью системной переменной в командной строке, либо вызвав Config.setSerialFilter. Не нужно указывать пользовательскую фабрику фильтров; по умолчанию используется встроенная фабрика фильтров. Встроенная фабрика фильтров предоставляет статический фильтр JVM для каждого ObjectInputStream.
Например, можно установить фильтр, который разрешает классы примеров, разрешает классы в модуле java.base и отклоняет все остальные классы: В качестве системной переменной командной строки:
% java -Djdk.serialFilter="example.*;java.base/*;!*" ...
var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*")
ObjectInputFilter.Config.setSerialFilter(filter);
В приложении с несколькими контекстами выполнения приложение может предоставить фабрику фильтров, чтобы защитить отдельные контексты, предоставляя настраиваемый фильтр для каждого. При создании потока фабрика фильтров вызывается для определения контекста выполнения из доступной информации, включая текущее состояние потока, иерархию вызывающих сторон, библиотеку, модуль и загрузчик классов. В этот момент политика фабрики фильтров по созданию или выбору фильтров может выбрать конкретный фильтр или состав фильтров на основе контекста. Фабрика фильтров десериализации JVM гарантирует, что контекстно-зависимый фильтр десериализации может быть установлен на каждом ObjectInputStream, и каждый считываемый из потока объект может быть проверен.
Вызов фабрики фильтров
Встроенная фабрика фильтров JVM — это функция, вызываемая при каждом создании ObjectInputStream и при установке контекстно-зависимого фильтра. Параметрами являются текущий фильтр и запрашиваемый фильтр, а возвращаемым значением — используемый фильтр для потока. При вызове из конструкторов ObjectInputStream первый параметр — null, а второй — статический фильтр JVM. При вызове из ObjectInputStream.setObjectInputFilter первый параметр — фильтр, текущий для потока (который был установлен в конструкторе), а второй — фильтр, переданный ObjectInputStream.setObjectInputFilter. Текущий и новый фильтры могут быть null, и фабрика может вернуть null. Обратите внимание, что реализация фабрики фильтров может также использовать любую контекстную информацию, например, извлеченную из контекста потока приложения или его стека вызовов, для составления и объединения нового фильтра. Она не ограничивается только своими двумя параметрами.
Активная фабрика фильтров десериализации либо:
- Приложению настраиваемая фабрика фильтров, заданная через
ObjectInputFilter.Config.setSerialFilterFactory(BinaryOperator)или системную переменнуюjdk.serialFilterFactoryили системную переменную безопасностиjdk.serialFilterFactory. - В противном случае встроенная фабрика фильтров десериализации предоставляет статический фильтр JVM при вызове из конструкторов ObjectInputStream и заменяет статический фильтр при вызове из
ObjectInputStream.setObjectInputFilter(ObjectInputFilter). См. getSerialFilterFactory.
Фильтры
Фильтры могут быть созданы из строки шаблона или на основе предиката класса для разрешения или отклонения классов.Метод checkInput(FilterInfo) фильтра вызывается ноль или более раз во время чтения объектов. Этот метод используется для проверки классов, длины каждого массива, количества считываемых из потока объектов, глубины графа и общего количества прочитанных из потока байтов.
Составные фильтры объединяют или проверяют результаты других фильтров. merge(filter, anotherFilter) фильтр объединяет значение статуса двух фильтров. rejectUndecidedClass(filter) проверяет результат фильтра для классов, когда статус равен UNDECIDED. Во многих случаях любой класс, не ALLOWED фильтром, должен быть REJECTED.
Фильтр десериализации определяет, разрешены или отклонены аргументы, и должен возвращать соответствующий статус: ALLOWED или REJECTED. Если фильтр не может определить статус, он должен вернуть UNDECIDED. Фильтры должны быть разработаны для конкретного случая использования и ожидаемых типов. Фильтр, разработанный для определенного использования, может получить класс за пределами области применения фильтра. Если цель фильтра — отклонить классы, то он может отклонить соответствующий класс и сообщить UNDECIDED для других. Фильтр может быть вызван с классом равным null, arrayLength равным -1, глубиной, количеством ссылок и размером потока и возвратить статус, отражающий только одно или только некоторые из значений. Это позволяет фильтру быть конкретным относительно сообщаемого выбора и использовать другие фильтры, не принуждая к статусам «разрешено» или «отклонено».
Примеры моделей фильтров
Для простых приложений может быть достаточно одного предопределенного фильтра, перечисляющего разрешенные или отклоненные классы, для управления риском десериализации неожиданных классов.Для приложения, состоящего из нескольких модулей или библиотек, структуру приложения можно использовать для определения классов, которые нужно разрешать или отклонять каждым ObjectInputStream в каждом контексте приложения. Фабрика фильтров десериализации вызывается при создании каждого потока и может проверить поток или программу, чтобы определить контекстно-зависимый фильтр, который следует применить. Некоторые возможные примеры:
- Локальное состояние потока может содержать фильтр, который необходимо применить или составить с контекстно-зависимым фильтром. Фильтры могут добавляться и удаляться из виртуального стека фильтров, поддерживаемого приложением или библиотеками.
- Фабрика фильтров может определить вызывающую сторону метода десериализации и использовать контекст модуля или библиотеки для выбора фильтра или составления соответствующего контекстно-зависимого фильтра. Механизм может идентифицировать вызывающую сторону с ограниченным или неограниченным доступом к сериализованным классам и соответственно выбрать фильтр.
Пример фильтрации каждой десериализации в потоке
Этот класс показывает, как фабрика фильтров, предоставленная приложением, может комбинировать фильтры для проверки каждой операции десериализации, выполняемой в потоке. Он определяет локальную переменную потока для хранения потокоспецифичного фильтра и строит фабрику фильтров, которая комбинирует этот фильтр со статическим глобальным фильтром JVM и потоково-специфичным фильтром, отклоняя все классы, не обработанные этими двумя фильтрами. Если установлен потоково-специфичный фильтр, который не принимает или не отклоняет класс, применяется комбинированный фильтр 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) {
// Merge to invoke the thread local filter and then the JVM-wide filter (if any)
filter = ObjectInputFilter.merge(filter, next);
return ObjectInputFilter.rejectUndecidedClass(filter);
}
return (next == null) ? null : ObjectInputFilter.rejectUndecidedClass(next);
} 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 merge to invoke it and then the current filter.
if (next != null) {
return ObjectInputFilter.merge(next, curr);
}
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 |
ObjectInputFilter.Config |
Утилитарный класс для установки и получения фабрики фильтра десериализации для всей JVM, статического фильтра для всей JVM или для создания фильтра по строке шаблона. |
static interface |
ObjectInputFilter.FilterInfo |
FilterInfo предоставляет доступ к информации о текущем объекте, который десериализуется, и статусу ObjectInputStream. |
static enum |
ObjectInputFilter.Status |
Статус проверки класса, длины массива, количества ссылок, глубины и размера потока. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
static ObjectInputFilter |
allowFilter |
Возвращает фильтр, который возвращает Status.ALLOWED, если предикат для класса является true. |
ObjectInputFilter.Status |
checkInput |
Проверка класса, длины массива, количества ссылок на объекты, глубины, размера потока и другой доступной информации для фильтрации. |
static ObjectInputFilter |
merge |
Возвращает фильтр, который объединяет статус фильтра и другого фильтра. |
static ObjectInputFilter |
rejectFilter |
Возвращает фильтр, который возвращает Status.REJECTED, если предикат для класса является true. |
static ObjectInputFilter |
rejectUndecidedClass |
Возвращает фильтр, который вызывает заданный фильтр и отображает 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)
Status.ALLOWED, если предикат по классу является true. Фильтр возвращает ALLOWED или otherStatus, основанный на предикате класса non-null и UNDECIDED, если класс является null. При вызове метода checkInput(info) фильтра, предикат применяется к info.serialClass(), статус возвращается следующим образом:
-
UNDECIDED, еслиserialClassявляетсяnull, -
ALLOWED, если предикат по классу возвращаетtrue, - В противном случае возвращается
otherStatus.
Пример создания фильтра, который позволит любому классу, загруженному из платформенного или bootstrap classloader.
ObjectInputFilter f
= allowFilter(cl -> cl.getClassLoader() == ClassLoader.getPlatformClassLoader() ||
cl.getClassLoader() == null, Status.UNDECIDED);
- Параметры:
-
predicate- предикат для проверки не-null класса -
otherStatus- статус, используемый, если предикат являетсяfalse - Возвращаемое значение:
- Фильтр, который возвращает
ALLOWED, если предикат по классу являетсяtrue - С:
- 17
rejectFilter
static ObjectInputFilter rejectFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)
Status.REJECTED, если предикат по классу является true. Фильтр возвращает REJECTED или otherStatus, основанный на предикате класса non-null и UNDECIDED, если класс является null. При вызове метода checkInput(info) фильтра, предикат применяется к serialClass(), возвращаемый статус: -
UNDECIDED, еслиserialClassявляетсяnull, -
REJECTED, если предикат по классу возвращаетtrue, - В противном случае возвращается
otherStatus.
Пример создания фильтра, который отклонит любой класс, загруженный из application classloader.
ObjectInputFilter f = rejectFilter(cl ->
cl.getClassLoader() == ClassLoader.ClassLoader.getSystemClassLoader(), Status.UNDECIDED);
- Параметры:
-
predicate- предикат для проверки не-null класса -
otherStatus- статус, используемый, если предикат являетсяfalse - Возвращаемое значение:
- возвращает фильтр, который возвращает
REJECTED, если предикат по классу являетсяtrue - С:
- 17
merge
static ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter)
another фильтр является null, возвращается filter. В противном случае, возвращается filter для объединения пары non-null фильтров. Возвращаемый фильтр реализует метод checkInput(FilterInfo) следующим образом: - Вызов
filterнаFilterInfoдля получения егоstatus; - Возврат
REJECTED, еслиstatusявляетсяREJECTED; - Вызов
anotherFilterдля полученияotherStatus; - Возврат
REJECTED, еслиotherStatusявляетсяREJECTED; - Возврат
ALLOWED, еслиstatusилиotherStatusявляетсяALLOWED, - В противном случае, возвращается
UNDECIDED
- Параметры:
-
filter- фильтр -
anotherFilter- фильтр, который нужно объединить с фильтром, может бытьnull - Возвращаемое значение:
- Интерфейс
ObjectInputFilter, объединяющий статусы фильтра и другого фильтра - С:
- 17
rejectUndecidedClass
static ObjectInputFilter rejectUndecidedClass(ObjectInputFilter filter)
UNDECIDED в REJECTED для классов, с некоторыми специальными случаями, в противном случае возвращает статус. Если класс не является примитивным классом и не является массивом, возвращаемый статус — REJECTED. Если класс является примитивным классом или классом массива, выполняются дополнительные проверки; см. список ниже для получения подробностей. Десериализация объектов принимает класс, если фильтр возвращает UNDECIDED. Добавление фильтра для отклонения неопределенных результатов для классов, которые не были ни разрешены, ни отклонены, может предотвратить прохождение классов через фильтр.
- Требования к реализации:
- Возвращаемый фильтр реализует метод
checkInput(FilterInfo)следующим образом:- Вызов фильтра на
FilterInfoдля получения егоstatus; - Возврат
status, если статус равенREJECTEDилиALLOWED; - Возврат
UNDECIDED, еслиfilterInfo.getSerialClass() serialClassявляетсяnull; - Возврат
REJECTED, если класс не является массивом; - Определение базового типа компонента, если
serialClassявляется массивом; - Возврат
UNDECIDED, если базовый тип компонента является примитивным классом; - Вызов фильтра на
base component typeдля получения егоcomponent status; - Возврат
ALLOWED, если статус компонента равенALLOWED; - В противном случае, возвращается
REJECTED.
- Вызов фильтра на
- Параметры:
-
filter- фильтр - Возвращаемое значение:
- Интерфейс
ObjectInputFilter, отображающий статусObjectInputFilter.Status.UNDECIDEDвObjectInputFilter.Status.REJECTEDдля классов, в противном случае возвращает статус фильтра - С:
- 17