javasecurityjaxbws-securityxxe

Prevent XXE Attack with JAXB


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?


Solution

  • 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