Rajith
Rajith

Reputation: 171

How to secure javax.xml.transform.TransformerFactory from XML external attacks

I have researched on the subject but couldn't find any relevant info regarding that

Do we need to take any security measurements to secure javax.xml.transform.Transformer against XML external entity attacks?

I did the following and it seems to expand the dtd.

String fileData = "<!DOCTYPE acunetix [  <!ENTITY sampleVal SYSTEM \"file:///media/sample\">]><username>&sampleVal;</username>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
StringWriter buff = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
System.out.println(buff.toString());

output contains the value from the file

<username>test</username>

Upvotes: 11

Views: 10675

Answers (2)

Jonathan Kibet
Jonathan Kibet

Reputation: 701

I know that this is an old post but for those who find themselves here, I hope is helps :)

After applying the solution below, SonarQube still complained with 'Disable access to external entities in XML parsing' security issue :(

factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

Eventually I landed on the solution below which finally fixed the issue for me.

TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

Upvotes: 0

coastalhacking
coastalhacking

Reputation: 307

Your code seems correct. When I run this slightly modified JUnit test case:

@Test
public void test() throws TransformerException, URISyntaxException {
  File testFile = new File(getClass().getResource("test.txt").toURI());
  assertTrue(testFile.exists());
  String fileData = "<!DOCTYPE acunetix [  <!ENTITY foo SYSTEM \"file://" + 
                    testFile.toString() +
                    "\">]><xxe>&foo;</xxe>";
  TransformerFactory transformerFactory = TransformerFactory.newInstance();
  System.out.println(transformerFactory.getClass().getName());
  transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  Transformer transformer = transformerFactory.newTransformer();
  StringWriter buff = new StringWriter();
  transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
  transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
  assertEquals("<xxe>&foo;</xxe>", buff.toString());
}

I get the following output:

com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
[Fatal Error] :1:182: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.
ERROR:  'External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.'

From the setFeature JavaDocs:

All implementations are required to support the XMLConstants.FEATURE_SECURE_PROCESSING feature. When the feature is:

  • true: the implementation will limit XML processing to conform to implementation limits and behave in a secure fashion as defined by the implementation. Examples include resolving user defined style sheets and functions. If XML processing is limited for security reasons, it will be reported via a call to the registered ErrorListener.fatalError(TransformerException exception). See setErrorListener(ErrorListener listener).

That error goes away if I comment out transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); and then the test fails because the entity is resolved.

Try adding an ErrorListener to both the TransformerFactory and Transformer:

transformerFactory.setErrorListener(new ErrorListener() {

  @Override
  public void warning(TransformerException exception) throws TransformerException {
    System.out.println("In Warning: " + exception.toString());
  }

  @Override
  public void error(TransformerException exception) throws TransformerException {
    System.out.println("In Error: " + exception.toString());
  }

  @Override
  public void fatalError(TransformerException exception) throws TransformerException {
    System.out.println("In Fatal: " + exception.toString());
  }
});

Transformer transformer = transformerFactory.newTransformer();
transformer.setErrorListener(transformerFactory.getErrorListener());

I see the following new console output now:

In Error: javax.xml.transform.TransformerException: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.

Maybe your implementation is treating it as a warning? Otherwise, maybe it's the implementation you're using? It looks like the JavaDoc spec isn't precise, so one implementation might do something different than another. I'd be interested to know faulty implementations!

Upvotes: 5

Related Questions