Avoid Security Loopholes Using @JsonView in Spring Boot
Don’t expose more than you think needs exposing
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.