Grails

Data binding issue, binding relationships when fields to bind explicitly set

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.2-M3
  • Fix Version/s: 1.2-RC2
  • Component/s: Security
  • Labels:
    None

Description

Spotted a issue with databinding when restricting parameters on a domain object, hasMay relationships are still bound.

From the documentation:

Data Binding and Security concerns

When batch updating properties from request parameters you need to be careful not to allow clients to bind malicious data to domain classes that end up being persisted to the database. You can limit what properties are bound to a given domain class using the subscript operator:

def p = Person.get(1)

p.properties['firstName','lastName'] = params

In this case only the firstName and lastName properties will be bound.

Given a basic class with a has many and not specifying it in the properties, the hasMany relationship is still created

User class:

 
class User {

    String username
    String forename
    String surname
    String email
    String passwordHash

    static hasMany = [roles : Role]

    static constraints = {
        username(nullable: false, blank: false, unique: true)
        forename(nullable: false, blank: false)
        surname(nullable: false, blank: false)
        passwordHash(nullable: false, blank: false, password: true)
        email(blank: false, email: true, unique: true)
    }
}

And a sample save method

 
    def save = {
        def userInstance = new User()

        //Only bind selected fields, we do not bind password as we need to
        //encrypt it. Roles should also not be bound as they are not listed in
        //the properties to bind
        userInstance.properties['username', 'email', 'forename', 'surname'] = params

        if(params.passwordHash){
            userInstance.passwordHash = encryptPassword(params.passwordHash)
        }

        if(!userInstance.hasErrors() && userInstance.save()) {
            flash.message = "User ${userInstance.id} created"
            redirect(action:show,id:userInstance.id)
        }
        else {
            render(view:'create',model:[userInstance:userInstance])
        }
    }

Passing a input element such as the below still gets bound and relationship created / save to the domain.

 
<input type="hidden" name="roles" value="1" />

I have tested this in 1.1.1 and 1.2-M3 and both allow this behavior.

Attached is a quick sample project demonstrating the issue. I'd expect the roles property of the User domain not to be set as it hasn't been explicitly set like the other fields.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: