Question

I'm using Spring 3.2.0. I have registered a few custom property editors for some basic needs as follows.

import editors.DateTimeEditor;
import editors.StrictNumberFormatEditor;
import java.math.RoundingMode;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import org.joda.time.DateTime;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public final class GlobalDataBinder 
{
    @InitBinder
    public void initBinder(WebDataBinder binder, WebRequest request)
    {
        binder.setIgnoreInvalidFields(true);
        binder.setIgnoreUnknownFields(true);
        //binder.setAllowedFields(someArray);
        NumberFormat numberFormat=DecimalFormat.getInstance();
        numberFormat.setGroupingUsed(false);
        numberFormat.setMaximumFractionDigits(2);
        numberFormat.setRoundingMode(RoundingMode.HALF_UP);

        binder.registerCustomEditor(DateTime.class, new DateTimeEditor("MM/dd/yyyy HH:mm:ss", true));
        binder.registerCustomEditor(Double.class, new StrictNumberFormatEditor(Double.class, numberFormat, true));
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
        binder.registerCustomEditor(URL.class, new URLEditor());
    } 
}

I have this many editors registered so far. Two of them DateTimeEditor and StrictNumberFormatEditor have been customized by overriding respective methods to fulfill custom needs of number format and Joda-Time.

Since I'm using Spring 3.2.0, I can take advantage of @ControllerAdvice.

Spring recommends to list a set of allowed fields with the setAllowedFields() method so that malicious users can not inject values into bound objects.

From the docs about DataBinder

Binder that allows for setting property values onto a target object, including support for validation and binding result analysis. The binding process can be customized through specifying allowed fields, required fields, custom editors, etc.

Note that there are potential security implications in failing to set an array of allowed fields. In the case of HTTP form POST data for example, malicious clients can attempt to subvert an application by supplying values for fields or properties that do not exist on the form. In some cases this could lead to illegal data being set on command objects or their nested objects. For this reason, it is highly recommended to specify the allowedFields property on the DataBinder.


I have a big application and obviously there are thousands of fields. Specifying and listing all of them with the setAllowedFields() is a tedious job. Additionally, somehow I need to remember them.

Changing a web page to remove some fields or add additional fields as the need arises again requires to modify the parameter value of the setAllowedFields() method to reflect those changes.

Is there any alternative to this?

Was it helpful?

Solution

Instead of using setAllowedFields() to white-list, you can use setDisallowedFields() to black-list. For example, from the petclinic sample application:

@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
    dataBinder.setDisallowedFields("id");
}

From a pure security standpoint white-listing is preferred to black-listing, but it maybe help ease the burden some.

OTHER TIPS

setAllowedFields() is very handy when using entity objects directly in web layer. Alternatively, one could use dedicated data transfer objects (DTO), from which entity objects are constructed in the service layer. Not only can the factories be re-used, but also used outside the web context, e.g. for asynchronous messages. Besides, DTO inheritance doesn't have to follow entity inheritance, so you are free to design the DTO hierarchy according to the needs of the use-cases.

from http://static.springsource.org/spring-webflow/docs/2.0.x/reference/htmlsingle/spring-webflow-reference.html#view-model

4.9. Specifying bindings explicitly

Use the binder element to configure the exact set of model bindings usable by the view. This is particularly useful in a Spring MVC environment for restricting the set of "allowed fields" per view.

<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="creditCard" />
        <binding property="creditCardName" />
        <binding property="creditCardExpiryMonth" />
        <binding property="creditCardExpiryYear" />
    </binder>
    <transition on="proceed" to="reviewBooking" />
    <transition on="cancel" to="cancel" bind="false" />
</view-state>

If the binder element is not specified, all public properties of the model are eligible for binding by the view. With the binder element specified, only the explicitly configured bindings are allowed.

Each binding may also apply a converter to format the model property value for display in a custom manner. If no converter is specified, the default converter for the model property's type will be used.

<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="checkinDate" converter="shortDate" />
        <binding property="checkoutDate" converter="shortDate" />    
        <binding property="creditCard" />
        <binding property="creditCardName" />
        <binding property="creditCardExpiryMonth" />
        <binding property="creditCardExpiryYear" />
    </binder>
    <transition on="proceed" to="reviewBooking" />
    <transition on="cancel" to="cancel" bind="false" />
</view-state>

In the example above, the shortDate converter is bound to the checkinDate and checkoutDate properties. Custom converters may be registered with the application's ConversionService.

Each binding may also apply a required check that will generate a validation error if the user provided value is null on form postback:

<view-state id="enterBookingDetails" model="booking">
    <binder>
        <binding property="checkinDate" converter="shortDate" required="true" />
        <binding property="checkoutDate" converter="shortDate" required="true" />
        <binding property="creditCard" required="true" />
        <binding property="creditCardName" required="true" />
        <binding property="creditCardExpiryMonth" required="true" />
        <binding property="creditCardExpiryYear" required="true" />
    </binder>
    <transition on="proceed" to="reviewBooking">
    <transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>

In the example above, all of the bindings are required. If one or more blank input values are bound, validation errors will be generated and the view will re-render with those errors.

A solution to use binder with DTO (companydata in example) in case most of the form input values should be converted to null if empty, but there is a need to add few exceptions (setDisallowedFields didn't work for me).

@InitBinder()
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}

@InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
    binder.registerCustomEditor(String.class, "companydata.companyName", new StringTrimmerEditor(false));
    binder.registerCustomEditor(String.class, "companydata.companyNumber", new StringTrimmerEditor(false));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top