Consider following model:
public class Contact {
@Required
public String name;
@Valid
public List<Information> informations;
}
public static class Information {
public String securedField;
@Required
public String email;
@Valid
public List<Phone> phones;
public static class Phone {
@Required
@Pattern(value = "[0-9.+]+", message = "A valid phone number is required")
public String number;
}
}
}
I don't want Information securedField
to be affected by mass assignment vulnerability. So i decided to set array of allowedFields for Contact Form
.
As i know, play forms are based on Spring DataBinder, so is it possible to handle collection fields? I don't want to write smth like:
- name
- informations[0].email
- informations[0].phones*
- informations[1].email
- informations[1].phones*
- etc
Following doesn't work:
- name
- informations.email
- informations.phones*
Should i extend existing Spring DataBinder
and Form
classes and override bind
method in this case?
Here's an arguably simpler solution. How about defining an extra constraint that will trigger a validation failure if the POST data contains any informations[%d].securedField
values?
import javax.validation.constraints.Null;
public static class Information {
@Null
public String securedField;
...
}
I think that this way you can call the default bindFromRequest
method instead of the one that accepts a whitelist of form field names, and still be protected against a mass assignment attack.
One shortcoming with this approach admittedly is that it would ultimately leak the names of your internal fields in the event of a concerted mass assignment attack. However if they had fairly bland, meaningless names such as securedField
(no offence intended!), I'm not sure how this information could be exploited by an attacker.
Edit
If you want to allow assignment to the field based on the current user type, maybe bean validation groups could help:
import javax.validation.constraints.Null;
public class Contact {
public interface Administrator {}
public interface User {}
...
public class Information {
@Null(groups = User.class)
public String securedField;
...
}
}
Controller code
...
final Form<Contact> contactForm;
if (currentUser.isAdministrator()) {
contactForm = form(Contact.class, Administrator.class).bindFromRequest();
} else {
contactForm = form(Contact.class, User.class).bindFromRequest();
}
...
If I understand your question correctly, you can use the following patterns to whitelist nested collection fields:
informations[*].email
informations[*].phones[*].*
i.e.
form.bindFromRequest("name", "informations[*].email", "informations[*].phones[*].*");