java - Запретить атаку XXE с JAXB




security ws-security (2)

JAXB

Вы можете предотвратить атаку Xml eXternal Entity (XXE), разобравшись с XMLStreamReader , у XMLInputFactory.SUPPORT_DTD свойства IS_SUPPORTING_EXTERNAL_ENTITIES и / или XMLInputFactory.SUPPORT_DTD установлены на false .

JAX-WS

Реализация JAX-WS должна позаботиться об этом для вас. Если это не так, я бы рекомендовал открыть ошибку против конкретной имментации.

ПРИМЕР

демонстрация

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

Этот XML-документ содержит объект, который был настроен для получения списка файлов, которые я использовал для создания этого примера.

<?xml version="1.0"?>
<!DOCTYPE customer
[
<!ENTITY name SYSTEM "/Users/bdoughan/Examples/src/xxe/">
]
>
<customer>
  <name>&name;</name>
</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;
    }

}

Выход - настройка по умолчанию

По умолчанию объект будет разрешен.

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

Вывод, когда XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES свойства XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES установлено значение false

Когда это свойство установлено, объект не разрешен.

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

Вывод, когда XMLInputFactory.SUPPORT_DTD свойства XMLInputFactory.SUPPORT_DTD установлено значение false

Когда это свойство установлено, создается исключение, пытающееся разрешить сущность.

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

Недавно у нас был аудит безопасности нашего кода, и одна из проблем заключается в том, что наше приложение подвержено атаке Xml EXternal Entity (XXE).

В принципе, приложение представляет собой калькулятор, который получает входные данные в виде XML через веб-службу.

Вот пример такой атаки XXE для нашего приложения:

<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>

Как вы можете видеть, мы можем ссылаться на объект, который указывает на внешний файл ( "file:///d:/" ).

Что касается самого входа XML (часть <calcinput>...</calcinput> ), он не привязан к JAXB (v2.1). Часть веб-сервиса основана на jaxws-rt (2.1).

Что мне нужно сделать для защиты моего веб-сервиса?

Благодарю.


Проблемы со многими из предложенных методов

Если вы собираетесь тестировать происхождение каталогов с os.path.commonprefix методов сравнения строк или os.path.commonprefix , они подвержены ошибкам с одинаково названными путями или относительными путями. Например:

  • /path/to/files/myfile будет отображаться как дочерний путь /path/to/file с использованием многих методов.
  • /path/to/files/../../myfiles не будет отображаться в качестве родителя /path/myfiles/myfile многими способами. На самом деле это так.

Предыдущий ответ Роб Денниса дает хороший способ сравнить происхождение пути, не сталкиваясь с этими проблемами. В Python 3.4 был добавлен модуль pathlib который может выполнять эти операции с более сложным способом, необязательно без ссылки на базовую ОС. jme описал в другом предыдущем ответе, как использовать pathlib для точного определения того, является ли один путь дочерним по отношению к другому. Если вы предпочитаете не использовать pathlib (не уверен, почему, это довольно здорово), то Python 3.5 ввел новый OS-метод в os.path который позволяет выполнять os.path проверки родительского родителя точно так же точно и без ошибок с намного меньшим количеством кода.

Новое для Python 3.5

Python 3.5 представил функцию os.path.commonpath . Это метод, специфичный для ОС, в котором работает код. Вы можете использовать commonpath следующим образом, чтобы точно определить происхождение пути:

def path_is_parent(parent_path, child_path):
    # Smooth out relative path names, note: if you are concerned about symbolic links, you should use os.path.realpath too
    parent_path = os.path.abspath(parent_path)
    child_path = os.path.abspath(child_path)

    # Compare the common path of the parent and child path with the common path of just the parent path. Using the commonpath method on just the parent path will regularise the path name in the same way as the comparison that deals with both paths, removing any trailing path separator
    return os.path.commonpath([parent_path]) == os.path.commonpath([parent_path, child_path])

Точный однострочный

Вы можете объединить всю партию в однострочный оператор if в Python 3.5. Это некрасиво, оно включает ненужные повторяющиеся вызовы на os.path.abspath и это определенно не поместится в руководящих принципах длины линии PEP 8 79 символов, но если вам нравится такая штука, здесь идет речь:

if os.path.commonpath([os.path.abspath(parent_path_to_test)]) == os.path.commonpath([os.path.abspath(parent_path_to_test), os.path.abspath(child_path_to_test)]):
    # Yes, the child path is under the parent path




java security jaxb ws-security xxe