Skip to content

Commit ee74adb

Browse files
committed
♻️ Move createSafeObjectInputStream to java 8 compatible class
1 parent 9239894 commit ee74adb

File tree

5 files changed

+104
-39
lines changed

5 files changed

+104
-39
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ dependencies {
6464
java11SourceSet.apiConfigurationName("commons-io:commons-io:2.11.0")
6565
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
6666
testImplementation("org.junit.jupiter:junit-jupiter-params")
67+
testImplementation("commons-fileupload:commons-fileupload:1.3.3")
6768
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
6869
testImplementation("org.hamcrest:hamcrest-all:1.3")
6970
testImplementation("org.mockito:mockito-core:4.0.0")

src/java11/main/io/github/pixee/security/ObjectInputFilters.java

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package io.github.pixee.security;
22

3-
import java.io.IOException;
4-
import java.io.InputStream;
53
import java.io.ObjectInputFilter;
64
import java.io.ObjectInputStream;
75
import java.util.Objects;
8-
import org.apache.commons.io.serialization.ValidatingObjectInputStream;
96

107
/**
11-
* This type exposes helper methods that will help defend against Java deserialization attacks.
8+
* This type exposes helper methods that will help defend against Java deserialization attacks
9+
* leveraging the Java 9+ API {@link ObjectInputFilter}.
1210
*
1311
* <p>For more information on deserialization checkout the <a
1412
* href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">OWASP
@@ -94,30 +92,6 @@ public Status checkInput(final FilterInfo filterInfo) {
9492
}
9593
}
9694

97-
/**
98-
* This method returns a wrapped {@link ObjectInputStream} that protects against deserialization
99-
* code execution attacks. This method can be used in Java 8 and previous.
100-
*
101-
* @param ois the stream to wrap and harden
102-
* @return an {@link ObjectInputStream} which is safe against all publicly known gadgets
103-
* @throws IOException if the underlying creation of {@link ObjectInputStream} fails
104-
*/
105-
public static ObjectInputStream createSafeObjectInputStream(final InputStream ois)
106-
throws IOException {
107-
try {
108-
final ValidatingObjectInputStream is = new ValidatingObjectInputStream(ois);
109-
for (String gadget : UnwantedTypes.dangerousClassNameTokens()) {
110-
is.reject("*" + gadget + "*");
111-
}
112-
return is;
113-
} catch (IOException e) {
114-
// ignored
115-
}
116-
117-
// if for some reason we can't replace it, we'll pass it back as it was given
118-
return new ObjectInputStream(ois);
119-
}
120-
12195
private static final ObjectInputFilter basicGadgetDenylistFilter =
12296
ObjectInputFilter.Config.createFilter(
12397
"!" + String.join("*;!", UnwantedTypes.dangerousClassNameTokens()));

src/java11Test/java/io/github/pixee/security/ObjectInputFiltersTest.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,6 @@ void default_is_unprotected() throws Exception {
4141
assertThat(o, instanceOf(DiskFileItem.class));
4242
}
4343

44-
@Test
45-
void validating_ois_works() throws Exception {
46-
ObjectInputStream ois =
47-
ObjectInputFilters.createSafeObjectInputStream(new ByteArrayInputStream(serializedGadget));
48-
assertThrows(
49-
InvalidClassException.class,
50-
() -> {
51-
ois.readObject();
52-
fail("this should have been blocked");
53-
});
54-
}
5544

5645
@Test
5746
void ois_harden_works() throws Exception {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.github.pixee.security;
2+
3+
import org.apache.commons.io.serialization.ValidatingObjectInputStream;
4+
5+
import java.io.IOException;
6+
import java.io.InputStream;
7+
import java.io.ObjectInputStream;
8+
9+
/**
10+
* This type exposes helper methods that will help defend against Java deserialization attacks
11+
* leveraging {@link ObjectInputStream} APIs.
12+
*
13+
* <p>For more information on deserialization checkout the <a
14+
* href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">OWASP
15+
* Cheat Sheet</a>.
16+
*/
17+
public final class SafeObjectInputStream {
18+
19+
/**
20+
* Private no-op constructor to prevent accidental initialization of this class
21+
*/
22+
private SafeObjectInputStream() {}
23+
24+
/**
25+
* This method returns a wrapped {@link ObjectInputStream} that protects against deserialization
26+
* code execution attacks. This method can be used in Java 8 and previous.
27+
*
28+
* @param ois the stream to wrap and harden
29+
* @return an {@link ObjectInputStream} which is safe against all publicly known gadgets
30+
* @throws IOException if the underlying creation of {@link ObjectInputStream} fails
31+
*/
32+
public static ObjectInputStream createSafeObjectInputStream(final InputStream ois)
33+
throws IOException {
34+
try {
35+
final ValidatingObjectInputStream is = new ValidatingObjectInputStream(ois);
36+
for (String gadget : UnwantedTypes.dangerousClassNameTokens()) {
37+
is.reject("*" + gadget + "*");
38+
}
39+
return is;
40+
} catch (IOException e) {
41+
// ignored
42+
}
43+
44+
// if for some reason we can't replace it, we'll pass it back as it was given
45+
return new ObjectInputStream(ois);
46+
}
47+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.github.pixee.security;
2+
3+
import org.apache.commons.fileupload.disk.DiskFileItem;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.io.ByteArrayInputStream;
8+
import java.io.ByteArrayOutputStream;
9+
import java.io.IOException;
10+
import java.io.InvalidClassException;
11+
import java.io.ObjectInputStream;
12+
import java.io.ObjectOutputStream;
13+
import java.nio.file.Files;
14+
15+
import static org.junit.jupiter.api.Assertions.assertThrows;
16+
import static org.junit.jupiter.api.Assertions.fail;
17+
18+
final class SafeObjectInputStreamTest {
19+
20+
private static DiskFileItem gadget; // this is an evil gadget type
21+
private static byte[] serializedGadget; // this the serialized bytes of that gadget
22+
23+
@BeforeAll
24+
static void setup() throws IOException {
25+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
26+
gadget =
27+
new DiskFileItem(
28+
"fieldName",
29+
"text/html",
30+
false,
31+
"foo.html",
32+
100,
33+
Files.createTempDirectory("adi").toFile());
34+
gadget.getOutputStream(); // needed to make the object serializable
35+
ObjectOutputStream oos = new ObjectOutputStream(baos);
36+
oos.writeObject(gadget);
37+
serializedGadget = baos.toByteArray();
38+
}
39+
40+
41+
@Test
42+
void validating_ois_works() throws Exception {
43+
ObjectInputStream ois =
44+
SafeObjectInputStream.createSafeObjectInputStream(new ByteArrayInputStream(serializedGadget));
45+
assertThrows(
46+
InvalidClassException.class,
47+
() -> {
48+
ois.readObject();
49+
fail("this should have been blocked");
50+
});
51+
}
52+
53+
54+
}

0 commit comments

Comments
 (0)