Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring's data binder autoGrowCollectionLimit doesn't work correctly

  • Problem:

    Spring's data binder allows you to set maximum size() of automatically created List<> e.g. to 3 items. It's quite easy to bypass this limitation and cause Spring to create a List of 3000+ items simply by modifying HTTP content sent to the server.

    In other words: while testing my webapp I was able by creating malicious HTTP request to force Spring's data binder to create a List<> consisting of 4000 items although I had set the limit to 3 items. This may easily lead to Out of Memory exceptions on any app server.

  • Question:

    Am I missing something obvious how to prevent Spring from doing this or rather it's a bug that should be reported to Spring's bug tracker?

  • Version used:

    spring-tool-suite-3.3.0.RELEASE,
    D:\m2\repo\org\springframework\spring-web\3.2.4.RELEASE\spring-web-3.2.4.RELEASE.jar

  • Description:

    I needed to bind multiple html <input /> elements to a single List<String> object, something like:

    <input type="text" name="phoneNumber[0]" />
    ...
    <input type="text" name="phoneNumber[n]" />
    

    Spring performs such conversion by default using org.springframework.beans.propertyeditors.CustomCollectionEditor. Below is a simple code snippet presenting the issue described above.

  • Code:

    • Backing bean:

    public class ContactDataEntity {
        private List<String> phoneNumber;
        // getters and setters
    }
    
    • Binder in controller:

    @RequestMapping(value = VIEW_PAGE_1, method = RequestMethod.POST)
    public String xxx(HttpServletRequest request, Model model) {
    
        // set and bind
        ContactDataEntity contactData = new ContactDataEntity();
        ServletRequestDataBinder binder = new ServletRequestDataBinder(contactData);
        binder.setAutoGrowCollectionLimit(3); // set limit to 3 items
        binder.bind(request);
    
        // test binding results
        List<String> numbers = contactData.getPhoneNumber();
        if (numbers != null) {
            System.out.print("numbers SIZE: " + numbers.size() + ", DATA: ");
            for (String s : numbers) System.out.print(s + ", ");
            System.out.print("\n");
        }
    
        // validate and return view name...
    
    }
    
  • Results for correct data (<= 3 items, everything works ok, I use Live HTTP Headers for Firefox):

    https://i.sstatic.net/AEjsA.jpg

  • Results for too many items (> 3 items, everything works ok, 500 Internal Server Error occurred):

    https://i.sstatic.net/MfWYy.jpg

  • Simple trick (> 3 items, no errors reported, sorry for my typo in 'overwritten'):

    https://i.sstatic.net/FNlXE.jpg

  • Let's exploit the above:

    https://i.sstatic.net/XPIhc.jpg

So, my question again: am I missing something obvious how to prevent Spring from doing this or rather it's a bug that should be reported to Spring's bug tracker?

// EDIT:
I reported it as a bug: https://jira.springsource.org/browse/SPR-11472

like image 777
dominik Avatar asked Sep 06 '25 03:09

dominik


1 Answers

Ok, until official fix in Spring 3.2.9 and 4.0.3 is released, I've overwritten Spring's default CustomCollectionEditor to fix this bug temporarily.

The only drawback is that you cannot use in your HTML code / HTTP request this:

&phoneNumber=0
&phoneNumber=1
&phoneNumber=2
&phoneNumber=3

but you rather need to index every parameter explicitly:

&phoneNumber[0]=0
&phoneNumber[1]=1
&phoneNumber[2]=2
&phoneNumber[3]=3

Multiple parameters without [] on the end are now simply ignored, see comment in the code below.

package xxx;

import java.util.List;

import org.springframework.beans.propertyeditors.CustomCollectionEditor;

/**
 * @see <a href="https://jira.springsource.org/browse/SPR-11472">https://jira.springsource.org/browse/SPR-11472</a>
 */
public class CustomListEditorSPR11472 extends CustomCollectionEditor {

    @SuppressWarnings("rawtypes")
    public CustomListEditorSPR11472(Class<List> collectionType) {
        super(collectionType);
    }

    @Override
    public void setValue(Object value) {
        /*
         * Force Spring to ignore all HTTP request **MULTIPLE** parameters without "[]" on the end so that
         * binder.setAutoGrowCollectionLimit() could work correctly. Example:
         *
         * phoneNumber[2]=2
         * Above request is OK, a List containing: 'null, null, 2' is created.
         *
         * phoneNumber=2
         * Above request is OK, **SINGLE** parameter without "[]", a List containing: '2' is created
         *
         * phoneNumber[0]=0&phoneNumber=1&phoneNumber=2
         * **MULTIPLE** parameters without "[]" are ignored, a List containing: '0' is created.
         */

        if ((value != null && value.getClass().isArray()) == false) {
            super.setValue(value);
        }
    }

}

Of course, you also need to register your CustomEditor in your binder:

binder.registerCustomEditor(List.class, new CustomListEditorSPR11472(List.class));

or more fine-grained version for a single property:

binder.registerCustomEditor(List.class, "phoneNumber", new CustomListEditorSPR11472(List.class));
like image 108
dominik Avatar answered Sep 07 '25 18:09

dominik