how to protect xml requests on a jersey server?

user2950720 :

I currently have a simple xml endpoint (example) created using the jersey-server 1.1 framework. it consumes and produces XML using the following notation:

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

however the endpoint is vulnerable to XXE attacks. (example) its also possible to get my server to talk to request any endpoint using this notation...

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

I want a way to protect the server and not allow it to call out to other servers/serve up files to attackers.

Is there a way to do this, since everything including the XML reading is coming from the framework itself ? @Consumes(MediaType.APPLICATION_XML)

The only way I think I could do this is to use regex on the body of the request somehow with a filter? to block DOCTYPE, SYSTEM, ENTITY requests and return an error but I am wondering is there a simpler way to do this and override the default behavior of @Consumes(MediaType.APPLICATION_XML)?

kolossus :

I'm going to address just the XXE concern because the question isn't entirely clear on other specific authentication/authorization concerns to address.

Starting from Blaise's approach to prevent XXE with basic JAXB, what needs to be done is to get lower-level access to the supplied XML. Good thing Jersey supports this out of the box. One way to do this is to replace your Employee argument with a StreamSource.

  1. First get a hold of the existing JAXBContext:

    private final @Context Providers providers; //full list of all providers available
    
  2. Modify your interface to accept a StreamSource so you have access to the raw incoming XML:

    @POST
    @Path("/post")
    @Consumes(MediaType.APPLICATION_XML)
    @Produces(MediaType.APPLICATION_XML)
    public Response getEmployee(StreamSource employeeStreamSource)
    
  3. Configure the JAXBContext unmarshaller to ignore DTDs:

    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();
    
    }
    

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=104980&siteId=1
Recommended