-->

Play framework 2 (Java) form data binding with nes

2020-04-16 06:19发布

问题:

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?

回答1:

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();
}
...


回答2:

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[*].*");