3

I'm trying to implement the workaround described in the Angular Docs for $http under JSON Vulnerability Protection. The workaround is to have the server prefix every JSON request with the string ")]}',\n".

My server is a JBoss running a RESTEasy web service. I've attempted to achieve the workaround using JAX-RS filters and interceptors to no avail.

Is it even possible to prefix every JSON response from RESTEasy with a string without modifying all the POJOs?

StarsSky
  • 6,721
  • 6
  • 38
  • 63
Ali Cheaito
  • 3,746
  • 3
  • 25
  • 30

4 Answers4

4

You can implement a custom MessageBodyWriter and serialize your POJOs on your own.

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonVulnerabilityWriter implements MessageBodyWriter<Object> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public long getSize(Object t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        entityStream.write(")]}',\n".getBytes("UTF-8"));
        entityStream.write(new ObjectMapper().writeValueAsBytes(obj));
    }

}
lefloh
  • 10,653
  • 3
  • 28
  • 50
  • 1
    I will add though that, since I'm using RESTEasy with the default Jackson JSON writer, might as well use ObjectMapper instead of gson - e.g: entityStream.write(new ObjectMapper().writeValueAsBytes(obj)); – Ali Cheaito May 09 '14 at 13:19
  • 1
    Good Point! I’ll update my answer. You could also extend ```JacksonJsonProvider``` or ```ResteasyJacksonProvider``` like [described here](http://stackoverflow.com/a/18794529) but this did not work for me on wildfly. – lefloh May 09 '14 at 19:05
2

I couldn't get Stefan's answer to work as Glassfish reported this:

java.lang.IllegalStateException: Stream provider is not defined. It must be set before writing first bytes to the entity output stream.

Instead I also rewrote SjefH's answer but using a generic WriterInterceptor:

import java.io.IOException;
import java.util.Objects;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

@Provider
public class AngularJsonInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
                    throws IOException, WebApplicationException {

        if (MediaType.APPLICATION_JSON.equals(Objects.toString(context.getMediaType()))) {
            context.getOutputStream().write(")]}',\n".getBytes("UTF-8"));
        }
        context.proceed();
    }
}
Community
  • 1
  • 1
1

I implemented a server interceptor, so I can use the existing serialization:

import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.spi.interception.MessageBodyWriterContext;
import org.jboss.resteasy.spi.interception.MessageBodyWriterInterceptor;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.util.Objects;

@Provider
@ServerInterceptor
public class AngularJsonVulnerabilityProtectionInterceptor implements MessageBodyWriterInterceptor {

  @Override
  public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException {
    if (MediaType.APPLICATION_JSON.equals(Objects.toString(context.getMediaType()))) {
      context.getOutputStream().write(")]}',\n".getBytes("UTF-8"));
    }
    context.proceed();
  }
}
SjefH
  • 11
  • 3
1

Found this thread via google and improved SjefH's solution so it doesn't need any resteasy-specific classes. This should work with plain JavaEE/JAX-RS.

import java.io.IOException;
import java.util.Objects;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

/**
 * Responsefilter that prefixes all JSON responses with ")]}',\n" for security reasons.
 * 
 * @see https://docs.angularjs.org/api/ng/service/$http "JSON Vulnerability"
 */
@Provider
public class AngularJsonVulnerabilityProtectionInterceptor implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
            throws IOException {
        boolean mediaTypeJSON = MediaType.APPLICATION_JSON.equals(Objects.toString(responseContext.getMediaType()));
        if (mediaTypeJSON) {
            responseContext.getEntityStream().write(")]}',\n".getBytes("UTF-8"));
        }
    }
}
Community
  • 1
  • 1
Stefan Ortgies
  • 295
  • 1
  • 2
  • 13