Reputation: 2115
Background:
I am using JAXB to unmarshal XML into Java objects. Originally, I was using just JAXB to perform the unmarshal. Then a static analysis was performed on the code and a high criticality issue was raised for XML External Entity Injection. After a little research, I found a suggestion (https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXB_Unmarshaller) to use a parser configured to prevent external entities from being parsed. An example of what to do was provided:
//Disable XXE
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
//Do unmarshall operation
Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(xml)));
JAXBContext jc = JAXBContext.newInstance(Object.class);
Unmarshaller um = jc.createUnmarshaller();
um.unmarshal(xmlSource);
I have not done this exactly as shown, but I believe I have done the same in effect:
XMLReader reader = getXMLReader();
if (reader == null) {
logger.warn("Unable to create XML reader");
return;
}
JAXBContext context = JAXBContext.newInstance(messageClass);
Unmarshaller unmarshaller = context.createUnmarshaller();
for (File file : files) {
try {
InputSource source = new InputSource(new FileReader(file));
Source xmlSource = new SAXSource(reader, source);
JAXBElement<? extends BaseType> object =
(JAXBElement<? extends BaseType>) unmarshaller.unmarshal(xmlSource);
messages.add(object.getValue());
} catch (FileNotFoundException e) {
logger.error("Exception", e);
}
}
...
private XMLReader getXMLReader() {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (SAXNotRecognizedException | SAXNotSupportedException
| ParserConfigurationException e) {
logger.error("Exception", e);
}
XMLReader reader = null;
try {
reader = factory.newSAXParser().getXMLReader();
} catch (SAXException | ParserConfigurationException e) {
logger.error("Exception", e);
}
return reader;
}
Problem:
After implementing the correction, I am now getting an unmarshal exception when the program attempts to read in XML:
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"ns1:TypeXYZ"). Expected elements are <{protected namespace URI}TypeABC>,...<{protected namespace URI}TypeXYZ>,...
Before the above fix where I was just using JAXB to unmarshal, it was able to properly parse the provided XML with no problem.
I assume that the SAX parser expects the XML to provide extra information that's missing, or that it needs to be configured to ignore whatever it's complaining about. I tried a few other "features" (http://xml.org/sax/features/namespace-prefixes=true and http://xml.org/sax/features/validation=false), but that did not resolve the problem.
I have no control over the XML schema that defines the XML types, nor do I have control over how the corresponding Java classes are generated.
Any information to help me understand what's going on and that helps me resolve this problem, would be very much appreciated.
Upvotes: 1
Views: 2005
Reputation: 2115
After a little experimentation I was able to resolve the error by setting the following features:
factory.setFeature("http://xml.org/sax/features/validation", false);
factory.setFeature("http://xml.org/sax/features/namespaces", true);
factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
Upvotes: 5