You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ra...@apache.org on 2026/10/02 04:08:53 UTC
(logging-log4j2) 02/07: Reject dynamic proxies in `FilteredObjectInputStream` (#4269)
This is an automated email from the ASF dual-hosted git repository.
ramanathan1504 pushed a commit to branch port/serialization-to-2.26.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit e8700bb8ee1d9804c5a069ce6632724e6b1fd11c
Author: Piotr P. Karwasz <pk...@apache.org>
AuthorDate: Wed Sep 30 12:19:31 2026 +0200
Reject dynamic proxies in `FilteredObjectInputStream` (#4269)
* Reject dynamic proxies in FilteredObjectInputStream
Dynamic proxy class descriptors are resolved through
`resolveProxyClass`, not `resolveClass`, so they bypassed the
deserialization allowlist on Java 8. The JEP 290 filter used on
Java 9 and later already rejects proxies, since their synthetic class
names never match the allowlist; this aligns the Java 8 path by
rejecting proxies outright.
Part of the hardening series from #4168.
Assisted-By: Claude Fable 5 <no...@anthropic.com>
* Shorten `resolveProxyClass` Javadoc
Assisted-By: Claude Opus 5.5 <no...@anthropic.com>
(cherry picked from commit a8578d1782327d23f8639311cfab81148307c500)
---
.../log4j/util/FilteredObjectInputStreamTest.java | 91 ++++++++++++++++++++++
.../log4j/util/FilteredObjectInputStream.java | 10 +++
.../.2.x.x/4269_reject_proxy_deserialization.xml | 12 +++
3 files changed, 113 insertions(+)
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/FilteredObjectInputStreamTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/FilteredObjectInputStreamTest.java
new file mode 100644
index 0000000000..5eece13827
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/FilteredObjectInputStreamTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InvalidObjectException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.logging.log4j.test.junit.SerialUtil;
+import org.junit.jupiter.api.Test;
+
+class FilteredObjectInputStreamTest {
+
+ private static FilteredObjectInputStream filteredStream(final byte[] data) throws Exception {
+ return new FilteredObjectInputStream(new ByteArrayInputStream(data));
+ }
+
+ @Test
+ void allow_listed_object_graph_round_trips() throws Exception {
+ final Map<String, Object> original = new HashMap<>();
+ original.put("string", "value");
+ original.put("integer", 17);
+ original.put("array", new int[] {1, 2, 3});
+ final Object restored =
+ filteredStream(SerialUtil.serialize((Serializable) original)).readObject();
+ assertEquals(original.keySet(), ((Map<?, ?>) restored).keySet());
+ }
+
+ @Test
+ void class_outside_allow_list_is_rejected() throws Exception {
+ final byte[] data = SerialUtil.serialize(new File("rejected"));
+ assertThrows(InvalidObjectException.class, () -> filteredStream(data).readObject());
+ }
+
+ @Test
+ void dynamic_proxy_is_rejected() throws Exception {
+ final byte[] data = SerialUtil.serialize((Serializable) serializableProxy());
+ assertThrows(InvalidObjectException.class, () -> filteredStream(data).readObject());
+ }
+
+ @Test
+ void allowed_extra_classes_do_not_re_enable_proxies() throws Exception {
+ final Object proxy = serializableProxy();
+ final byte[] data = SerialUtil.serialize((Serializable) proxy);
+ final FilteredObjectInputStream stream = new FilteredObjectInputStream(
+ new ByteArrayInputStream(data),
+ Collections.singleton(proxy.getClass().getName()));
+ assertThrows(InvalidObjectException.class, stream::readObject);
+ }
+
+ private static Object serializableProxy() {
+ return Proxy.newProxyInstance(
+ FilteredObjectInputStreamTest.class.getClassLoader(),
+ new Class<?>[] {Comparator.class},
+ new SerializableInvocationHandler());
+ }
+
+ private static class SerializableInvocationHandler implements InvocationHandler, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) {
+ return null;
+ }
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java
index 5cb93cee4b..c1a35fae1d 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java
@@ -105,6 +105,16 @@ public class FilteredObjectInputStream extends ObjectInputStream {
return super.resolveClass(desc);
}
+ /**
+ * Unconditionally rejects dynamic proxy classes.
+ *
+ * <p>No supported Log4j serialized form contains a dynamic proxy.</p>
+ */
+ @Override
+ protected Class<?> resolveProxyClass(final String[] interfaces) throws IOException, ClassNotFoundException {
+ throw new InvalidObjectException("Proxy classes are not allowed for deserialization");
+ }
+
private static boolean isAllowedByDefault(final String name) {
return isRequiredPackage(name) || REQUIRED_JAVA_CLASSES.contains(name);
}
diff --git a/src/changelog/.2.x.x/4269_reject_proxy_deserialization.xml b/src/changelog/.2.x.x/4269_reject_proxy_deserialization.xml
new file mode 100644
index 0000000000..5e5386371a
--- /dev/null
+++ b/src/changelog/.2.x.x/4269_reject_proxy_deserialization.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ https://logging.apache.org/xml/ns
+ https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="changed">
+ <issue id="4269" link="https://github.com/apache/logging-log4j2/pull/4269"/>
+ <description format="asciidoc">
+ `FilteredObjectInputStream` now rejects dynamic proxy classes.
+ </description>
+</entry>