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

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

12 Comments

  1. test February 28, 2015
  2. Ritz August 12, 2014
  3. Aitor Alejandro July 11, 2014
    • Ryan July 13, 2014
  4. Shook February 23, 2014
    • Ryan February 23, 2014
  5. arjun prajapati December 18, 2013
    • Ryan December 18, 2013
  6. kulthana November 24, 2013
    • Ryan November 24, 2013
  7. mohit jain July 25, 2013

Leave a Reply

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