47

Recently, we had a security audit on our code, and one of the problem is that our application is subject to the Xml eXternal Entity (XXE) attack.

Basically, the application is a calculator that receives inputs as XML, through a Web-Service.

Here is an example of such an XXE attack on our application:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <foo:calculateStuff>
         <!--Optional:-->
         <xmlInput><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE currency [  
   <!ENTITY include SYSTEM "file:///d:/" >]>
<calcinput>...</calcinput>
]]></xmlInput>
      </foo:calculateStuff>
   </soapenv:Body>
</soapenv:Envelope>

As you can see, we can refer to an entity that points to an external file ("file:///d:/").

Regarding the XML input itself (the <calcinput>...</calcinput> part) is unmarshalled with JAXB (v2.1). The web-service part is based on jaxws-rt (2.1).

What do I need to do to secure my web-service?

BSMP
  • 4,596
  • 8
  • 33
  • 44
Romain Linsolas
  • 79,475
  • 49
  • 202
  • 273

1 Answers1

67

JAXB

You can prevent the Xml eXternal Entity (XXE) attack by unmarshalling from an XMLStreamReader that has the IS_SUPPORTING_EXTERNAL_ENTITIES and/or XMLInputFactory.SUPPORT_DTD properties set to false.

JAX-WS

A JAX-WS implementation should take care of this for you. If it doesn't I would recommend opening a bug against the specific implmententation.


EXAMPLE

Demo

package xxe;

import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        XMLInputFactory xif = XMLInputFactory.newFactory();
        xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
        xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
        XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/xxe/input.xml"));

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer customer = (Customer) unmarshaller.unmarshal(xsr);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

input.xml

This XML document contains an entity that has been setup to get the listing of files I used to create this example.

<?xml version="1.0"?>
<!DOCTYPE customer
[
<!ENTITY name SYSTEM "/Users/bdoughan/Examples/src/xxe/">
]
>
<customer>
  <name>&name;</name>
</customer>

Customer

package xxe;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Output - Default Configuration

By default the entity will be resolved.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <name>Customer.java
Demo.java
input.xml
</name>
</customer>

Output when XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES property is set to false

When this property is set the entity is not resolved.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <name></name>
</customer>

Output when XMLInputFactory.SUPPORT_DTD property is set to false

When this property is set an exception is thrown trying to resolve the entity.

Exception in thread "main" javax.xml.bind.UnmarshalException
 - with linked exception:
[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.]
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:436)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:372)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:342)
    at xxe.Demo.main(Demo.java:18)
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.
    at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:598)
    at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:196)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:370)
    ... 2 more
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks for your really detailed answer! I have still a little issue: when I set `xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); `, I get the following `ParseError`: **The entity "include" was referenced, but not declared**. But if I remove this property (but keep the first one), then the XML is correctly parsed. Any idea? – Romain Linsolas Oct 22 '12 at 09:05
  • 1
    @BlaiseDoughan is there a way to enable entities, but limit them to a value say 5 or 10? We receive requests which have "&" in some fields which the user inputs. – asgs Nov 19 '13 at 18:33
  • 1
    @asgs - You can specify a VM level entity expansion limit. See: http://blog.bdoughan.com/2011/03/preventing-entity-expansion-attacks-in.html – bdoughan Nov 19 '13 at 18:37
  • @BlaiseDoughan, is it safe/thread-safe to create one securely configured XMLInputFactory and then create a new XMLStreamReader for each instance document? – doughgle Mar 21 '16 at 01:44
  • I have a spring MVC webservice in which i have configured marshalling and unmarshalling as above but I am still getting XML external entity injection in a tool Acunetix .Please help ! – shraddha bhardwaj Mar 31 '16 at 05:55
  • @romaintaz You wrote, that you want to secure the web-service. How can you use the `XMLInputFactory xif = XMLInputFactory.newFactory();` ? I mean - the parsing is done by the container and what you get is an object... – user1414745 Aug 04 '16 at 11:20
  • [This](https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SAXTransformerFactory) website explains how to block it on several major Java frameworks. – Sameer Puri Aug 10 '16 at 20:51
  • That works fine, i have tried the way mentioned in https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet but it didn't work.. thanks a lot ! – Shessuky Dec 27 '18 at 09:13
  • @bdoughan you are using input.xml as a string but i am using SoapHeader class object like xif.createXMLStreamReader(soapHeader.getSource()), still i am getting same exception in kiwan tool "XML enetity Injection". – Hitesh Nov 06 '19 at 06:53
  • @user1414745 when i convert soapHeader.getSource() into string using TransformerFactory object then its give me error `java.lang.IllegalArgumentException: Unknown configuration property http://javax.xml.XMLConstants/property/accessExternalDTD` – Hitesh Dec 06 '19 at 17:47
  • @bdoughan Not sure if you will see this.. but I am using ByteArrayInputStream in unmarshal(). Is it possible to use your suggested answer and use XMLStreamReader? Also, can you elaborate on input.xml file? My question can be found: https://stackoverflow.com/questions/65295943/prevent-xxe-attack-with-jaxb-xmlstreamreader – Jonathan Hagen Dec 14 '20 at 20:25