如何保护泽西服务器上的xml请求?

时间:2019-05-29 20:09:18

标签: java xml jersey xxe

我目前有一个使用jersey-server 1.1框架创建的简单xml端点(示例)。它使用以下表示法消费并生成XML:

@POST
@Path("/post")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Response getEmployee(Employee employee) {
     return Response.status(Status.OK).entity(employee).build();
}

但是端点容易受到XXE攻击。 (例) 也可以让我的服务器使用此符号进行对话以请求任何终结点...

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY % a SYSTEM "file:///etc/passwd">
%a;
]>

我想要一种保护服务器的方法,不允许它调出其他服务器/为攻击者提供文件。

由于包括XML读取在内的所有内容都来自框架本身,因此有办法做到吗? @Consumes(MediaType.APPLICATION_XML)

我认为我能做到这一点的唯一方法是使用过滤器以某种方式在请求主体上使用正则表达式?阻止DOCTYPESYSTEMENTITY请求并返回错误,但我想知道是否有更简单的方法来执行此操作并覆盖@Consumes(MediaType.APPLICATION_XML)的默认行为?

3 个答案:

答案 0 :(得分:1)

我将仅解决XXE问题,因为对于要解决的其他特定身份验证/授权问题尚不完全清楚。

Blaise's approach to prevent XXE with basic JAXB开始,需要做的是获得对所提供XML的低级访问。好东西泽西开箱即用。一种实现方法是将Employee参数替换为StreamSource

  1. 首先获取现有的JAXBContext

    private final @Context Providers providers; //full list of all providers available
    
  2. 修改您的界面以接受StreamSource,以便您可以访问原始的传入XML:

    @POST
    @Path("/post")
    @Consumes(MediaType.APPLICATION_XML)
    @Produces(MediaType.APPLICATION_XML)
    public Response getEmployee(StreamSource employeeStreamSource)
    
  3. 配置JAXBContext解组器以忽略DTD:

    public Response getEmployee(StreamSource employeeStreamSource){
        //we try to get a hold of the JAXBContext
        ContextResolver<JAXBContext> jaxbResolver = provider.getContextResolver(JAXBContext.class, MediaType.APPLICATION_XML_TYPE);
        JAXBContext jaxbContext= null;
        if(null != jaxbResolver) {
            jaxbContext = jaxbResolver.getContext(Employee.class);
        }
        if(null == jaxbContext) {
            jaxbContext = JAXBContext.newInstance(Employee.class);
        }
    
        XMLInputFactory xif = XMLInputFactory.newFactory();
        xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); //Don't blindly parse entities
        xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); //suppress DTD
        XMLStreamReader xsr = xif.createXMLStreamReader(employeeStreamSource); //beging parsing incoming XML
    
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        Employee employee= (Employee) unmarshaller.unmarshal(xsr);  //manually unmarshal
    
        return Response.status(Status.OK).entity(employee).build();
    
    }
    

答案 1 :(得分:0)

您需要将http://apache.org/xml/features/nonvalidating/load-external-dtdhttp://xml.org/sax/features/validation功能禁用为described in details

  /* Set features to turn off loading of external DTDs. */
    mDocumentBuilderFactoryDelegateBuilderFactory.setFeature(
       LOADEXTERNALDTD_FEATURE, false);
    mDocumentBuilderFactoryDelegateBuilderFactory.setFeature(
       XML_VALIDATION_FEATURE, false);
     

可以在运行时以编程方式操作Java系统属性,我想到了用包装器动态替换当前文档构建器工厂的想法,该包装器在文档构建器工厂的新实例上设置了某些功能。据说设置这些功能会导致在解析包含此类引用的XML时忽略对DTD文档的引用。

Shorter code

  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     dbf.setValidating(false);
     dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
     DocumentBuilder db = dbf.newDocumentBuilder();
     Document doc = db.parse(iStream);

答案 2 :(得分:0)

我遇到了同样的问题,不幸的是,上述解决方案仅适用于仅具有 XML 传输类型的服务。同时支持 XML 和 JSON 的服务的实现效率不高。

我在 Github 上的 jersey 存储库中发现了此讨论 https://github.com/eclipse-ee4j/jersey/issues/3446,该问题似乎已在 2020 年 3 月以来的 2.33 版本中得到解决:

<块引用>

将 Jersey 依赖升级到 2.33

我认为这比修改每项服务要容易得多。