Avoid Security Loopholes Using @JsonView in Spring Boot

Don’t expose more than you think needs exposing

Parul Dhingra
Better Programming

--

Spring Boot logo

If a certain property on an object is internal to your business and not useful to a consumer, then don’t return it.

Let’s say we’re using the controller to query information and return it to the front end in the JSON data format. Often, some username and password queries are involved in the JSON data, but for security reasons, we may not need all of the User object user information (for example, username and password) to be returned to the front end.

But when we use the @RestController annotation, the returned User object is automatically converted to the corresponding JSON array and transmitted to the front end. We can’t remove the unnecessary JSON information, such as the username and password, and then return it. In order to solve this JSON data-control problem, we can use the JsonView annotation for development.

Let’s Learn With an Example

Sometimes you may want to reuse the same form class for receiving request data in multiple handlers/controllers.

For example, say you have this UserForm for user registration:

public class UserForm {

private String username;

private String name;

private String password;

// Getters and Setters ...
}

The user-registration handler looks like the following:

@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public UserDto signup(@RequestBody UserForm userForm){
return userService.signup(userForm);
}

Now, say you have another handler for updating users, looking like this:

@PutMapping("/users/{id}")
public UserDto updateUser(@pathVariable long id, @RequestBody UserForm userForm) {

return userService.update(id, userForm);
}

Notice the same userForm above?

Consequently, someone can call the endpoint with the unrequired fields (e.g., username and password), thus injecting those into your form.

This is where the @JsonView annotation could be really useful. You can define @JsonView on fields that need to be included in the API response.

But the question is: Gow do we inform the Jackson processor to consider only certain fields based on the API?

How to Use @JsonView

We can use @JsonView to limit or control-field display for different users. You can decide which properties to be serialized based on the View class provided in the JsonView annotation. Sounds interesting, right?

To prevent the kind of injection discussed above, first define a marker interface, as below:

public interface UpdateUser {}

Then, in userForm, annotate the fields you want to receive when updating with @JsonView(UpdateUser.class):

public class UserForm {

private String username;

@JsonView(UpdateUser.class)
private String name;

private String password;

// Getters and Setters ...
}

Now our model is ready, but this isn’t enough. We should make sure our REST API methods are annotated with @JsonView, as shown below. So finally, use the same annotation in the handler, as below:

@PutMapping("/users/{id}")
public UserDto updateUser(@pathVariable long id,
@RequestBody @JsonView(UpdateUser.class) UserForm userForm) {

return userService.update(id, userForm);
}

That’s all you need to prevent the injection.

Conclusion

This is very useful, particularly when you’re reusing the domain classes as forms. For example, if you reuse a User domain class as the form and then save it straight to the database, you can get hacked, and this is where the real trouble comes in.

When registering, what if a malicious user adds an id or a createdDate field, that gets injected instead of autogenerated?

Take a few minutes and ponder over the question before you jump to a conclusion.

I hope you learned something from this article. Also please share if there are better alternatives. See you in another article soon.

--

--