mirror of
https://github.com/google/nomulus
synced 2026-08-15 11:46:08 +00:00
Harden XML parsing, serialization, and randomness (#3075)
This commit introduces several security hardening improvements across the codebase: 1. XML Processing: Hardened `TransformerFactory` and `SchemaFactory` instantiations in `EppMessage.java` by explicitly enabling `XMLConstants.FEATURE_SECURE_PROCESSING` and disabling external schema access. 2. Randomness: Replaced instances of `java.util.Random` with `java.security.SecureRandom` in `SelfSignedCaCertificate.java` for stronger entropy. (Added documentation in `ProxyModule.java` explaining why `java.util.Random` is intentionally retained there for metrics sampling). 3. Deserialization: Hardened `SerializeUtils.java` by injecting an `ObjectInputFilter` into the `ObjectInputStream`, restricting deserialization strictly to expected `google.registry` classes and standard Java collections.
This commit is contained in:
@@ -27,7 +27,6 @@ import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
@@ -45,7 +44,7 @@ public class SelfSignedCaCertificate {
|
||||
|
||||
private static final String DEFAULT_ISSUER_FQDN = "registry-test";
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
|
||||
private static final KeyPairGenerator keyGen = createKeyPairGenerator();
|
||||
private static final ImmutableMap<String, String> KEY_SIGNATURE_ALGS =
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.io.BaseEncoding.base16;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputFilter.Config;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
@@ -60,7 +61,13 @@ public final class SerializeUtils {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return type.cast(new ObjectInputStream(new ByteArrayInputStream(objectBytes)).readObject());
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(objectBytes));
|
||||
// Restrict deserialization to known, trusted packages to prevent RCE gadget chain attacks.
|
||||
ois.setObjectInputFilter(
|
||||
Config.createFilter(
|
||||
"google.registry.**;com.google.common.**;java.**;javax.**;jakarta.**;"
|
||||
+ "org.hibernate.**;org.joda.**;com.googlecode.objectify.**;!*"));
|
||||
return type.cast(ois.readObject());
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to deserialize: objectBytes=" + base16().encode(objectBytes), e);
|
||||
|
||||
@@ -20,9 +20,12 @@ import static google.registry.util.SerializeUtils.parse;
|
||||
import static google.registry.util.SerializeUtils.serialize;
|
||||
import static google.registry.util.SerializeUtils.stringify;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.Serializable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
/** Unit tests for {@link SerializeUtils}. */
|
||||
class SerializeUtilsTest {
|
||||
@@ -111,4 +114,19 @@ class SerializeUtilsTest {
|
||||
assertThrows(NullPointerException.class, () -> parse(String.class, null));
|
||||
assertThat(thrown).hasMessageThat().contains("Object string cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeserialize_unauthorizedClass_isRejectedByFilter() {
|
||||
// AssertionFailedError implements Serializable but is NOT in the
|
||||
// whitelist of allowed deserialization packages.
|
||||
AssertionFailedError untrustedObject = new AssertionFailedError("test");
|
||||
|
||||
try {
|
||||
deserialize(Object.class, serialize(untrustedObject));
|
||||
fail("Expected an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e).hasCauseThat().isInstanceOf(InvalidClassException.class);
|
||||
assertThat(e).hasCauseThat().hasMessageThat().contains("REJECTED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user