I am implementing a rest-full Web service with Jersey which accepts http request from client in json form.
On fortify scan I am getting critical issue :-"mass assignment insecure binder configuration".
I want to bind the json values in htttp request to the model class in my server side code and since it is a small module I want to avoid using Spring MVC framework.
Below is my code snippet which is working fine but I need to map json request to model class below without using Spring MVC.
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST**(JsonObject model**, #Context HttpServletRequest request) {
System.out.println(model);
return Response.status(200).entity(model).build();
}
This is the model class :-
public class ActivateService {
public String mWalletToken;
public String topMerchantEMPID;
public String serviceCategory;
}
I checked these links , however the answer is more specific to Spring MVC fmwrk:
What is the solution for Mass Assignment: Insecure Binder Configuration Vulnerability?
How to fix Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) in java
This can be implemeted via Jacksonson . Jackson is one of the best JSON Providers/parsers and can be used with Jersey in Rest implemetation.The REST services will produce and consume JSON and the JSON serialization and de-serialization happens automatically behind the scenes
Create View class as :
public class View {
public static class Editable {}
public static class Viewable extends Editable {}
public static class Internal extends Viewable {}
}
Create Model class as :
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "activateService")
public class ActivateService implements Serializable {
#JsonView(View.Editable.class)
public String mWalletToken;
#JsonView(View.Editable.class)
public String topMerchantEMPID;
#JsonView(View.Editable.class)
public String serviceCategory;
}
and the Rest -full web service method :
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST(#JsonView(View.Editable.class) final ActivateService model, #Context HttpServletRequest request) {
In JAX-RS, if one model(either request or response) is annotated with #JsonView(View.Editable.class), in our case add method, Jackson will only serialize or deserialize fields that are annotated with #JsonView(View.Editable.class).
In our case, client can only pass editableField, if client pass any other fields, server will just silently ignore them.
Use below dependencies in pom.xml
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-
databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-json -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19.4</version>
</dependency>
Source :- https://www.owasp.org/index.php/Mass_Assignment_Cheat_Sheet
and http://lifelongprogrammer.blogspot.com/2015/09/using-jackson-view-to-protect-mass-assignment.html
it also works just by placing this in the pojo or model class
#JsonIgnoreProperties(ignoreUnknown=true)
public class ActivateService {
[...]
}
resource:
https://stackoverflow.com/a/39013609/8782229
How can I dynamically create an URL for a Controller action?
Consider the following case:
#Controller
#RequestMapping("controller")
public class Controller {
#RequestMapping("url")
public String method() {
return "Whatever"
}
}
What I'd like to do is get the base URL and concat controller/url to it.
For this behavior, Laravel for example provides the URL helper (action() method). Is there something similar in Spring Boot?
you can use UriComponentsBuilder to get the current url and concat the additional part to it
#Controller
#RequestMapping("controller")
public class Controller {
#RequestMapping("url")
public String method(UriComponentBuilder ucb) {
URI uri = ucb.path("/url").build().toUri();
return "Whatever"
}
}
There is a library present for spring boot framework. Which you need to add in your project in order to generate link dynamically. The gradle dependency of this library is given below.
compile 'org.springframework.boot:spring-boot-starter-hateoas:2.1.4.RELEASE'
I am assuming your build system is gradle but if you are using maven then please use below syntax.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
After than you can generate link dynamically as below.
WebMvcLinkBuilder.linkTo(Controller.class).slash("url").withSelfRel().getHref();
I think using #PathVariable will help you
#Controller
#RequestMapping(value="/controller")
public class Controller {
#RequestMapping(value="/url/{id}", method=RequestMethod.GET)
public String method(#PathVariable("id") String id) {
System.out.println("the url value : "+id );
return "Whatever"
}
}
then you can call the method using /controller/url/{here the value}
example /controller/url/www.google.com
if you want to map only Controller , using a property file
application.properties
server.context-path=/rest
If your Controllers are serving Data from a Repository, then Spring Data REST can take out much of the boilerplate & solve your initial problem.
Spring Data REST
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
You can control the base URL by using a property file.
application.properties
spring.data.rest.basePath=/rest
That's you want /rest concat controller/url
I am trying to test a Jax-rs resource by following this https://jersey.java.net/documentation/latest/test-framework.html,
and I am using container jersey-test-framework-provider-jdk-http
I can assert status code. However, when I try to readEntity, I get exception:
javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:39)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:50)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:248)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:217)
My Resource Class:
#Path("/")
public class SampleResource {
#GET
#Path("/health")
#Produces(MediaType.APPLICATION_JSON)
public String getServiceStatus() {
return "{\"Status\": \"OK\"}";
}
}
My Test Class:
public class TestSampleResource extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class);
}
#Test
public void testHealthEndpoint() {
Response healthResponse = target("health").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(200, healthResponse.getstatus()); // works
String body = healthResponse.readEntity(String.class);
Assert.assertEquals("{\"Status\": \"OK\"}", body);
}
}
Can anyone please help?
The problem comes from having both Jersey and RestEasy client on the classpath. When you call target() on the JerseyTest, the WebTarget is obtained from a Client that is built by calling ClientBuilder.newClient().
The ClientBuilder is a standard JAX-RS API, and it is implemented first to search for an implementation of ClientBuilder through the META-INF/services files, looking for a file named javax.ws.rs.client.ClientBuilder, whose content is the name of an implementation of the ClientBuilder. If no such file is found, it defaults to looking for JerseyClientBuilder.
jersey-client has no such file META-INF/services/javax.ws.rs.core.ClientBuilder because it's ClientBuilder is the default for JAX-RS client. If you look in your resteasy-client jar, you will see the it does have that file. And if you look in the contents of that file, you will see the ResteasyClientBuilder as the implementation.
So even though you are using Jersey's test framework, the Client being used, is RESTeasy's implementation. And I guess all the standard configurations with entity providers never gets configured. Conversion between String and application/json is one of those standard providers you need in your case.
I would say just explicitly use Jersey client implementation. You will no longer be able to call target on the JerseyTest. You will need to explicitly create the client
#Test
public void dotest() {
final Client client = new JerseyClientBuilder().build();
WebTarget target = client.target("http://localhost:9998");
final Response response = target.path("health").request().get();
final String json = response.readEntity(String.class);
}
The default base path for JerseyTest is http://localhost:9998, so I explicitly create the WebTarget with that.
Note that I said the String to/from application/json is supported by standard providers. So if you will only be serializing Strings, then you don't need anything else. If you want POJO serialization support for the Jersey client (and server side), you should add the following
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
I suspect the json parser in your test is being misguided by the presence of curly braces. Basically it thinks you are returning a json object, not a json string. Try returning "Status:OK"
As the exception says you are missing a MessageBodyReader for content-type application/json. Do you have JacksonJsonProvider on your classpath? It can be added as a dependency to jackson-jaxrs-json-provider:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.3</version>
</dependency>
Then register the JacksonJsonProvider in your test application:
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class, JacksonJsonProvider.class);
}
this is my javascript:
function getWeather() {
$.getJSON('getTemperature/' + $('.data option:selected').val(), null, function(data) {
alert('Success');
});
}
this is my controller:
#RequestMapping(value="/getTemperature/{id}", headers="Accept=*/*", method = RequestMethod.GET)
#ResponseBody
public Weather getTemparature(#PathVariable("id") Integer id){
Weather weather = weatherService.getCurrentWeather(id);
return weather;
}
spring-servlet.xml
<context:annotation-config />
<tx:annotation-driven />
Getting this error:
GET http://localhost:8080/web/getTemperature/2 406 (Not Acceptable)
Headers:
Response Headers
Server Apache-Coyote/1.1
Content-Type text/html;charset=utf-8
Content-Length 1070
Date Sun, 18 Sep 2011 17:00:35 GMT
Request Headers
Host localhost:8080
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0.2) Gecko/20100101 Firefox/6.0.2
Accept application/json, text/javascript, */*; q=0.01
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection keep-alive
X-Requested-With XMLHttpRequest
Referer http://localhost:8080/web/weather
Cookie JSESSIONID=7D27FAC18050ED84B58DAFB0A51CB7E4
Interesting note:
I get 406 error, but the hibernate query works meanwhile.
This is what tomcat log says, everytime when I change selection in dropbox:
select weather0_.ID as ID0_0_, weather0_.CITY_ID as CITY2_0_0_, weather0_.DATE as DATE0_0_, weather0_.TEMP as TEMP0_0_ from WEATHER weather0_ where weather0_.ID=?
What could the problem be? There were two similar questions in SO before, I tried all the accepted hints there, but they did not work I guess...
Any suggestions? Feel free to ask questions...
406 Not Acceptable
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
So, your request accept header is application/json and your controller is not able to return that. This happens when the correct HTTPMessageConverter can not be found to satisfy the #ResponseBody annotated return value. HTTPMessageConverter are automatically registered when you use the <mvc:annotation-driven>, given certain 3-d party libraries in the classpath.
Either you don't have the correct Jackson library in your classpath, or you haven't used the
<mvc:annotation-driven> directive.
I successfully replicated your scenario and it worked fine using these two libraries and no headers="Accept=*/*" directive.
jackson-core-asl-1.7.4.jar
jackson-mapper-asl-1.7.4.jar
I had same issue, with Latest Spring 4.1.1 onwards you need to add following jars to pom.xml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1.1</version>
</dependency>
also make sure you have following jar:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
406 Spring MVC Json, not acceptable according to the request "accept" headers
There is another case where this status will be returned: if the Jackson mapper cannot figure out how to serialize your bean. For example, if you have two accessor methods for the same boolean property, isFoo() and getFoo().
What's happening is that Spring's MappingJackson2HttpMessageConverter calls Jackson's StdSerializerProvider to see if it can convert your object. At the bottom of the call chain, StdSerializerProvider._createAndCacheUntypedSerializer throws a JsonMappingException with an informative message. However, this exception is swallowed by StdSerializerProvider._createAndCacheUntypedSerializer, which tells Spring that it can't convert the object. Having run out of converters, Spring reports that it's not being given an Accept header that it can use, which of course is bogus when you're giving it */*.
There is a bug for this behavior, but it was closed as "cannot reproduce": the method that's being called doesn't declare that it can throw, so swallowing exceptions is apparently an appropriate solution (yes, that was sarcasm). Unfortunately, Jackson doesn't have any logging ... and there are a lot of comments in the codebase wishing it did, so I suspect this isn't the only hidden gotcha.
I had the same problem, my controller method executes but response is Error 406.
I debug AbstractMessageConverterMethodProcessor#writeWithMessageConverters and found that method ContentNegotiationManager#resolveMediaTypes always returns text/html which is not supported by MappingJacksonHttpMessageConverter. The problem is that the org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy works earlier than org.springframework.web.accept.HeaderContentNegotiationStrategy, and extension of my request /get-clients.html is the cause of my problem with Error 406. I just changed request url to /get-clients.
Make sure that the sent object (Weather in this case) contains getter/setter
Make sure that following 2 jar's are present in class path.
If any one or both are missing then this error will come.
jackson-core-asl-1.9.X.jar jackson-mapper-asl-1.9.X.jar
Finally found answer from here:
Mapping restful ajax requests to spring
I quote:
#RequestBody/#ResponseBody annotations don't use normal view resolvers, they use their own HttpMessageConverters. In order to use these annotations, you should configure these converters in AnnotationMethodHandlerAdapter, as described in the reference (you probably need MappingJacksonHttpMessageConverter).
Check <mvc:annotation-driven /> in dispatcherservlet.xml , if not add it.
And add
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
these dependencies in your pom.xml
Probably no one is scrolling down this far, but none of the above solutions fixed it for me, but making all my getter methods public did.
I'd left my getter visibility at package-private; Jackson decided it couldn't find them and blew up. (Using #JsonAutoDetect(getterVisibility=NON_PRIVATE) only partially fixed it.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
<version>2.6.3</version>
</dependency>
I was having the same problem because I was missing the #EnableWebMvc annotation. (All of my spring configurations are annotation-based, the XML equivalent would be mvc:annotation-driven)
In the controller, shouldn't the response body annotation be on the return type and not the method, like so :
#RequestMapping(value="/getTemperature/{id}", headers="Accept=*/*", method = RequestMethod.GET)
public #ResponseBody Weather getTemparature(#PathVariable("id") Integer id){
Weather weather = weatherService.getCurrentWeather(id);
return weather;
}
I'd also use the raw jquery.ajax function, and make sure contentType and dataType are being set correctly.
On a different note, I find the spring handling of json rather problematic. It was easier when I did it all myself using strings, and GSON.
As #atott mentioned.
If you have added the latest version of Jackson in your pom.xml, and with Spring 4.0 or newer, using #ResponseBody on your action method and #RequestMapping configured with produces="application/json;charset=utf-8", however, you still got 406(Not Acceptable), I guess you need to try this in your MVC DispatcherServlet context configuration:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
</bean>
That's the way how I resolved my issue finally.
check this thread.
spring mvc restcontroller return json string
p/s: you should add jack son mapping config to your WebMvcConfig class
#Override
protected void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// put the jackson converter to the front of the list so that application/json content-type strings will be treated as JSON
converters.add(new MappingJackson2HttpMessageConverter());
// and probably needs a string converter too for text/plain content-type strings to be properly handled
converters.add(new StringHttpMessageConverter());
}
Spring 4.3.10: I used the below settings to resolve the issue.
Step 1: Add the below dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Step 2: Add the below in your MVC DispatcherServlet context configuration:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false"/>
<property name="favorParameter" value="true"/>
<property name="ignoreAcceptHeader" value="false" />
</bean>
Since spring 3.2, as per the default configuration favorPathExtension is set as true, because of this if the request uri have any proper extensions like .htm spring will give priority for the extension. In step 2 I had added the contentNegotiationManager bean to override this.
make sure your have correct jackson version in your classpath
Check as #joyfun did for the correct version of jackson but also check our headers ... Accept / may not be transmitted by the client ... use firebug or equivalent to check what your get request is actually sending. I think the headers attribute of the annotation /may/ be checking literals although I'm not 100% sure.
Other then the obvious problems I had another one that I couldn't fix regardless of including all possible JARs, dependancies and annotations in Spring servlet. Eventually I found that I have wrong file extension by that I mean I had two separate servlet running in same container and I needed to map to different file extensions where one was ".do" and the other as used for subscriptions was randomly named ".sub". All good but SUB is valid file extension normally used for films subtitle files and thus Tomcat was overriding the header and returning something like "text/x-dvd.sub..." so all was fine but the application was expecting JSON but getting Subtitles thus all I had to do is change the mapping in my web.xml file I've added:
<mime-mapping>
<extension>sub</extension>
<mime-type>application/json</mime-type>
</mime-mapping>
I had the same problem unfortunately non of the solution here solved my problem as my problem was something in a different class.
I first checked that all dependencies are in place as suggested by #bekur
then I checked the request/response that travels from clients to the server all headers was in place an properly set by Jquery.
I then checked the RequestMappingHandlerAdapter MessageConverters and all 7 of them were in place, I really started to hate Spring ! I then updated to from Spring 4.0.6.RELEASE to 4.2.0.RELEASE I have got another response rather than the above. It was Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type
Here is my controller method
#RequestMapping(value = "/upload", method = RequestMethod.POST,produces = "application/json")
public ResponseEntity<UploadPictureResult> pictureUpload(FirewalledRequest initialRequest) {
DefaultMultipartHttpServletRequest request = (DefaultMultipartHttpServletRequest) initialRequest.getRequest();
try {
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext()) {
MultipartFile file = request.getFile(iterator.next());
session.save(toImage(file));
}
} catch (Exception e) {
return new ResponseEntity<UploadPictureResult>(new UploadPictureResult(),HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<UploadPictureResult>(new UploadPictureResult(), HttpStatus.OK);
}
public class UploadPictureResult extends WebResponse{
private List<Image> images;
public void setImages(List<Image> images) {
this.images = images;
}
}
public class WebResponse implements Serializable {
protected String message;
public WebResponse() {
}
public WebResponse(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
}
The solution was to make UploadPictureResult not to extend WebResponse
For some reason spring was not able to determine the how to convert UploadPictureReslt when it extended WebResponse
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.0</version>
</dependency>
i don't use ssl authentication and this jackson-databind contain jackson-core.jar and jackson-databind.jar, and then change the RequestMapping content like this:
#RequestMapping(value = "/id/{number}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
public #ResponseBody Customer findCustomer(#PathVariable int number){
Customer result = customerService.findById(number);
return result;
}
attention:
if your produces is not "application/json" type and i had not noticed this and got an 406 error, help this can help you out.
This is update answer for springVersion=5.0.3.RELEASE.
Those above answers will be only worked older springVersion < 4.1 version. for latest spring you have to add following dependencies in gradle file:
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: fasterxmljackson
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: fasterxmljackson
fasterxmljackson=2.9.4
I hope this will be helpful for who using latest spring version.
Simple answer just add Getter method in your domain/model class.
But Why this works ??
Under the hood Spring used HttpMessageConverters to convert your input JSON to Java Object. The Accept header that is passed in the request is used to select appropriate MessageConvertor at runtime. These message convertors use getter of your domain/model class for conversion, so if there are no getter method, Marshall and unmarshall Java Objects to and from JSON will not happen, even if you add Jackson in your classpath, because even Jackson lib uses Getter methods for marshalling stuffs !!.
Can you remove the headers element in #RequestMapping and try..
Like
#RequestMapping(value="/getTemperature/{id}", method = RequestMethod.GET)
I guess spring does an 'contains check' rather than exact match for accept headers. But still, worth a try to remove the headers element and check.