Spring MVC form validation with HDIV (HTTP Data Integrity Validator)

HDIV is a cool site addition to help secure web applications. Out of the box it will help prevent cross site request forgery (CSRF), remove the ability to alter non-editable data (hidden fields, params…) and even has options to limit characters globally across form fields. Having used it a project I found that the documentation can be hard to follow and there are implications when attempting to mix in Spring validation via the @Valid annotation. This is a quick guide to setup the basic features and tips when wanting to extending into custom field validation.

This is based on a very simple sample Spring MVC app referenced in an earlier post.

Our goal will be to secure the site so only the form page can be directly accessed. The success page should redirect to the form with an error if the first page is skipped. We will also add a global filter to prevent unauthorized characters (SQL injection, XSS, bad data…) from being entered into any field using the @Valid annotation.

First add the required dependencies to the project pom.xml. For this example we will use HDIV version 2.1.3. 2.1.4-SNAPSHOT has some nice enhancements but changes and considerations are necessary.

<!-- HDIV -->
  <dependency>
      <groupId>org.hdiv</groupId>
      <artifactId>hdiv-core</artifactId>
      <version>2.1.3</version>
  </dependency>

  <dependency>
      <groupId>org.hdiv</groupId>
      <artifactId>hdiv-config</artifactId>
      <version>2.1.3</version>
  </dependency>

  <dependency>
      <groupId>org.hdiv</groupId>
      <artifactId>hdiv-spring-mvc</artifactId>
      <version>2.1.3</version>
  </dependency>

  <dependency>
      <groupId>org.hdiv</groupId>
      <artifactId>hdiv-jstl-taglibs-1.2</artifactId>
      <version>2.1.3</version>
  </dependency>

<!-- Validation JSR-303 support for @Valid -->
  <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.0.0.GA</version>
  </dependency>

  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>4.3.1.Final</version>
  </dependency>

Create the hdiv-config.xml file in /src/main/resources/

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:hdiv="http://www.hdiv.org/schema/hdiv"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.hdiv.org/schema/hdiv http://www.hdiv.org/schema/hdiv/hdiv.xsd">


    <hdiv:config excludedExtensions="css,png,gif,jpeg,jpg,js" errorPage="/security-error" randomName="true">
        <hdiv:startPages>/,/index</hdiv:startPages>
    </hdiv:config>

    <!-- Accepted pattern within the application for all editable parameters (generated from textbox and textarea) -->
    <hdiv:validation id="safeText">
        <hdiv:acceptedPattern><![CDATA[^[a-zA-Z0-9@.-_]*$]]></hdiv:acceptedPattern>
    </hdiv:validation>

    <!-- Finally, it's necessary to define editable data validation list for
         the application -->
    <hdiv:editableValidations>
        <hdiv:validationRule url="/.*">safeText</hdiv:validationRule>
    </hdiv:editableValidations>


</beans>

The documentation on the HDIV site is good for creating this file.

This file defines a few beans for with easy syntax. The config class is set to exclude common static files (css,png…) from the security filter, errorPage is where the 301 redirect will send the client when a HDIV violation is detected. RandomName refers to the parameter name that HDIV will use to track the page token. On forms this is the last hidden input in the form, on regular pages it’s appended to the param string.

Start pages will generate the CSRF token and allow users to directly load the page without needing the token first.

The random name does make it a little harder to load test with JMeter as you will need to use an XPath extractor with a value of “//input[@type="hidden"][last()]/@value” to get the token. With JMeter 2.9 you can use the CSS/jQuery extractor with expression “input[type=hidden]“, attribute “value” and match number to last hidden index. Then you will need to use the token & param name to pass in on the next request.

The next beans are for field validation. This gives all the features for SQL injection & XSS prevention. The id of the validation declaration is used to map urls. The acceptedPattern above is the default/recommended for text boxes.

Editable validation defines the URL’s that will filter all form input. This is at best restricted to a page, field level would need to be handled in the controller to bindings.

At this stage it’s defined but not active until we setup the HDIV filter in web.xml and wire it up to Spring in the servlet-config.

<context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>
           classpath*:/applicationContext.xml
           classpath:/hdiv-config.xml
       </param-value>
   </context-param>

....

<!-- HDIV Filters -->
    <filter>
        <filter-name>ValidatorFilter</filter-name>
        <filter-class>org.hdiv.filter.ValidatorFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>ValidatorFilter</filter-name>
        <servlet-name>app</servlet-name>
    </filter-mapping>

....

<!-- HDIV Listener -->
<listener>
    <listener-class>org.hdiv.listener.InitListener</listener-class>
</listener>

Add hdiv-config.xml to the contextConfigLocation params. This will let Spring load the beans. Next add the filters so HDIV can interact with the session and params. Lastly add the listener and this file is done.

Next wire the text validations into Spring. This will get the hasErrors() to respond and get the HDIV message out to the form:errors tag.


<bean id="editableValidator" class="org.hdiv.web.validator.EditableParameterValidator"/>

<mvc:annotation-driven validator="editableValidator" />

This above will enable @Valid to use HDIV instead of the default hibernate validator. The hibernate validator is loaded automatically from the classpath and gives you access to javax JSR-303 validation annotations. But in this case we will not be using them and delegate it all to HDIV.

Java Config Update (4/8/13)
I have another post about converting the servlet & web.xml to use springs new java config option. If you want to port HDIV over to use that method then follow the steps in the Servlet to Java Config post and apply the following:


// This overrides the javax field validator with HDIV
// Remove this block to disable HDIV field validation
@Override
public Validator getValidator() {
    return getParameterValidator();
}

@Bean
public EditableParameterValidator getParameterValidator() {
    return new EditableParameterValidator();
}

HDIV Version 2.1.4

This config changes with HDIV version 2.1.4-SNAPSHOT. The editableValidator is already registered as a bean named “hdivEditableValidator” so only adding the validation to the mvc tag is necessary. Also worth noting that the new version will check for a JSR-303 validation in the classpath and if it exists it will delegate successful validations to it (like a validation chain of 2). Only frustrating thing is that there is no way at this time to specify order or priority so if you have a nice email field validation message but another field fails HDIV validation, then only the HDIV message is shown. For small apps I suggest using HDIV for all except field validation, instead use the hibernate validator and javax annotations. This adds risk as you will need to ensure all fields are handled properly but the tradeoff is more control over the user experience.

Update the controller form post method to check validations. The second method is the HDIV redirect on error which adds a basic message to the model.

@RequestMapping(value = "/create", method = RequestMethod.POST)
public String search(Model model, @Valid SignupForm signupForm, BindingResult result, RedirectAttributes redirectAttributes) {

    if (result.hasErrors()) {
        return "index";
    }
    
	// Process or save the form here
	
	return "show";
}

...

@RequestMapping(value = "/security-error", method = RequestMethod.GET)
public String securityError(RedirectAttributes redirectAttributes) {
    redirectAttributes.addFlashAttribute("page_error", "You do have have permission to do that!");
    return "redirect:/";
}

Here is the simple form. Notice there is no declaration for HDIV specifically in the form. This will be added dynamically so we don’t need to worry about it.

The first part will check for a page level error and display the text if necessary.

<c:if test="${page_error != null }">
       <div class="alert alert-error">
           <button type="button" class="close" data-dismiss="alert">&times;</button>
           <h4>Error!</h4>
               ${page_error}
       </div>
</c:if>

<form:form method="post" modelAttribute="signupForm" action="create" id="signupForm" cssClass="form-horizontal"
           autocomplete="off">
    <fieldset>
        <legend>Enter Your Information</legend>

        <c:set var="emailErrors"><form:errors path="email"/></c:set>
        <div class="control-group<c:if test="${not empty emailErrors}"> error</c:if>">
            <label class="control-label" for="field-email">Email</label>

            <div class="controls">
                <form:input path="email" id="field-email" tabindex="1" maxlength="45" placeholder="Email"/>
                <form:errors path="email" cssClass="help-inline" element="span"/>
            </div>
        </div>
        <c:set var="firstNameErrors"><form:errors path="firstName"/></c:set>
        <div class="control-group<c:if test="${not empty firstNameErrors}"> error</c:if>">
            <label class="control-label" for="field-firstName">First Name</label>
            <div class="controls">
                <form:input path="firstName" id="field-firstName" tabindex="2" maxlength="35" placeholder="First Name"/>
                <form:errors path="firstName" cssClass="help-inline" element="span"/>
            </div>
        </div>
        <c:set var="lastNameErrors"><form:errors path="lastName"/></c:set>
        <div class="control-group<c:if test="${not empty lastNameErrors}"> error</c:if>">
            <label class="control-label" for="field-lastName">Last Name</label>
            <div class="controls">
                <form:input path="lastName" id="field-lastName" tabindex="3" maxlength="35" placeholder="Last Name"/>
                <form:errors path="lastName" cssClass="help-inline" element="span"/>
            </div>
        </div>
        <div class="form-actions">
            <button type="submit" class="btn btn-primary">Sign up</button>
            <button type="button" class="btn">Cancel</button>
        </div>
    </fieldset>
</form:form>

When the app is run and the /create is accessed without the form you will get the following error message at the top of the page. This will also show up for any other HDIV non field error like form tampering or bad sessions.

hdiv_security_error

Try entering a script and it will return a field error

hdiv_field_error

Posted in Security, Spring MVC, Tech Stuff Tagged with: , ,
10 comments on “Spring MVC form validation with HDIV (HTTP Data Integrity Validator)
  1. mohit jain says:

    Hi Lucky, its really good tutorial and helped me a lot…. it would be great if you share the sample code link…..

  2. kulthana says:

    Hi,
    How you are validating the incoming request parameter (form parameter’s), you are calling any validate on incoming request or Hdiv injecting internally ?

    • Ryan says:

      HDIV gets loaded at two levels. It is defined as a servlet filter & listener in web.xml. The config in hdiv-config.xml directs HDIV in how to handle params on matching requests. Start pages will create CSRF tokens, Safe Text is used to globally screen user input, any non matching character would redirect to an error. There is flexibility with safe text to create different patterns per page/form or area of the site. I am also using the Spring @Valid annotation in the controller method which invokes HDIV validation (spring validator is set to use HDIV in app-servlet.xml) on request binding.

  3. arjun prajapati says:

    hey ryan,

    can you post a example to prevent url tampering using hdiv ???

    • Ryan says:

      Do you have an example of what you are trying to filter or prevent? Any page that is not listed as a start page will be checked for a token, editable params and valid entries if the field is editable. If any of those fail (via GET or POST) then you will get an exception.

      If the intent is to secure a normal view (not a form post) that has params (/view?id=1234 or /view/1234), and is expected to be directly accessible, then that would be considered a starting page and not filtered or checked for a CSRF token. At that point the Spring annotation for @Valid on the param to restrict harmful input and proper internal data handling should be sufficient.

  4. Shook says:

    Hi Ryan,

    Am newbie for hdiv filter and wanna ask you about page filtering. Ive created 1 page and when I click on the URL, its always redirected to the error page. I dont know where to edit and how hdiv filter works. Appreciate if you could share with me on how can I create new page w/o getting access denied.
    Looking forward to hear from you ..

    Cheers
    Shook

    • Ryan says:

      Hi Shook,

      The first page should be defined as a start page, which means that it does not accept any params (or handles posts) and is expected to generate a token what is used to validate the next request. Start pages are defined in hdiv-config.xml.

  5. Hi, do you think that is possible to use HDIV in a project that has spring, struts, tiles and traditional javaee servlets deployed with maven??

  6. Ritz says:

    Hi Ryan,
    I’m using Struts MVC with 2.1.8 version and somewhere it is said that the compatible HDIV version is not available for that.
    Will you suggest any solution for that or I need to use Spring MVC…?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>