Description
java.beans.XMLDecoder reconstructs object creation and method calls rather than simply parsing XML data. Interpreting untrusted input can let an attacker perform unintended operations with the application's permissions. The risk concerns operations executed while interpreting the document, not just the call to the decoder's constructor.
Potential impact
- Code execution: Input-selected constructors or methods can perform operations with the process's permissions.
- Sensitive data access: Data and system resources available to the application may be exposed.
- Data manipulation and service disruption: Input can modify object state or consume processing resources.
Remediation
- Do not use
XMLDecoderwith untrusted data. Change the protocol to a data format such as JSON with an explicit model containing only the required fields. - Do not assume that a custom
ClassLoaderor class allow-list makesXMLDecodersafe. - Validate fields, types, and size; do not accept arbitrary class names or polymorphic type information. If XML is required, use a data parser rather than object reconstruction, and restrict DTDs and external entities.
Examples
Before
import java.beans.XMLDecoder;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
public class InsecureXMLDecoder {
public static Object decode(String untrustedXml) {
try (XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream(
untrustedXml.getBytes(StandardCharsets.UTF_8)))) {
return decoder.readObject();
}
}
}
The input can select objects and operations. Creating an object and actually starting a process are different operations.
After
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class MessageReader {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static String readMessage(String json) throws IOException {
if (json == null || json.length() > 4096) {
throw new IllegalArgumentException("Invalid message size");
}
JsonNode data = MAPPER.readTree(json);
if (data == null || !data.isObject() || data.size() != 1
|| !data.path("message").isTextual()) {
throw new IllegalArgumentException("Expected a message string");
}
return data.get("message").textValue();
}
}
The Jackson JSON tree permits only one message string. Arbitrary type reconstruction is not enabled. This is a protocol change, not a compatible replacement for XML object serialization: callers must send JSON. Limit request-body size and enforce access permissions separately at the calling boundary.