Avoiding mass assignment vulnerability a in Play Framework and DropWizard

Play Framework comes equipped with a pretty complete object-relational mapping (ORM) features to enable fast and easy exchange of data between web forms and database models. As usual, with fast and easy comes the risk of abuse and Play is no exception here.

tl;dr: always explicitly list the parameters you want to get from the user in bindFromRequest() parameters, so e.g. bindFromRequest("user", "email") etc.

Play uses Ebean as the base for its ORM implementation and it's indeed easy to use. Let's say we have an example, simplified Java model:

public class Item extends Model {
    public Long id;
    public String title;
    public boolean admin = false;
}

We can now rather trivially make a form out of it in our HTML template. Note that we do not allow the users to set their admin status from the form because, well, they shouldn't be able to nominate themselves admins:

@helper.form(action=routes.Application.add_item()){
    @helper.inputText(itemForm("title"))
}

And then on the receiving side the controller will repack the form data into the database Item object:

public static Result add_item_play() {
        Form<Item> itemForm = Form.form(Item.class);
        Item item = itemForm.bindFromRequest().get();
        item.save();
        return redirect("/");
    }

As simple as it is, you should be very careful with running bindFromRequest() on user-supplied data, as you can easily end up being vulnerable to mass assignment (CWE-915). This is because the method will happily accept any input from the user and attempt to map it into the destination model without checking if it was really what the application wants to do.

So if the original form submission was /add_item?title=something the ORM will take the title parameter and attempt to map it into Item.title. If the malicious form submission is /add_item?title=something&admin=true then the ORM will do exactly the same, creating the item with the "hidden" admin field set to true.

Solution to that problem is rather simple: the bindFromRequest method takes optional parameters naming the fields that you are explicitly allowed to be used in mapping. In our case it should be just one, so the respective part of controller will look like this:

    Item item = itemForm.bindFromRequest("title").get();

As result, any other injected fields will be just ignored. Please note that this is just one way of getting data into typical Play application and there are more. Play is commonly extended with DropWizard to build RESTful APIs and the same problem will apply to the method acceptting data as a JSON block.

Examples in this article were taken from an intentionally vulnerable Play application called play-esapi I've created as playground for testing vulnerabilities and safeguards in Play apps.