/ JSTL, SECURITY, SPRING MVC

Sanitizing webapp outputs as an an afterthought

For sure, software security should be part of every developer’s requirements: they should be explained and detailed before development. Unfortunately, it happens in real life that this is not always the case. Alternatively, even when it is, developers make mistakes and/or have to make with tight (read impossible) plannings. In the absence of security checks automated tools, sooner or later, an issue will appear.

I’ve been thinking about a way to sanitize the output of a large-scale legacy Spring MVC application in a reliable way (i.e. not go on each page to fix issues). Basically, there are 4 ways output is displayed in the HTML page.

# Name Sample snippet Description

1

Form taglib

<form:input path="firstName">

Outputs a bean attribute

2

Spring taglib

<spring:message code="label.name.first">

Outputs a message from a properties file

3

Java Standard Taglib Library

<c:out value="${pageContext.request.requestURI}" />

Outputs a value

4

Expression Language

<span>${pageContext.request.requestURI}</span>

Outputs a value

Spring taglibs

Spring taglibs are a breeze to work with. Basically, Spring offers multiple ways to sanitize the output, each scope parameter having a possibility to be overridden by a narrower one:

  1. Application scoped, with the boolean defaultHtmlEscape context parameter
    <context-param>
      <param-name>defaultHtmlEscape</param-name>
      <param-value>true</param-value>
    </context-param>
  2. Page scoped (i.e. all forms on page), with the <spring:defaultHtmlEscape> tag
  3. Tag scoped, with the htmlEscape attribute of the tag

There’s only one catch; the <spring:message> tag can take not only a code (the key in the property file) but also arguments - however, those are not escaped:

Hello, ${0} ${1}

A possible sanitization technique consists of the following steps:

  1. Create a new SanitizeMessageTag:
    • Inherit from Spring’s MessageTag
    • Override the relevant revolveArguments(Object) method
    • Use the desired sanitization technique (Spring uses its own HtmlUtils.htmlEscape(String))
  2. Copy the existing Spring TagLib Descriptor and create a new one out of it
  3. Update it to bind the message tag to the newly created SanitizeMessageTag class
  4. Last but not least, override the configuration of the taglib in the web deployment descriptor:
    <jsp-config>
      <taglib>
        <taglib-uri>http://www.springframework.org/tags</taglib-uri>
        <taglib-location>/WEB-INF/tld/sanitized-spring-form.tld</taglib-location>
      </taglib>
    </jsp-config>

    By default, the JavaEE specifications mandates for the container to look for TLDs insides JARs located under the WEB-INF/lib directory. It is also possible to configure them in the web deployment descriptor. However, the configuration takes precedence over automatic scanning.

This way, existing JSP using the Spring taglib will automatically benefit from the new tag with no page-to-page update necessary.

JSTL

The <c:out> tag works the same way as the <spring:message> one, the only difference being there’s no global configuration parameter, only a escapeXml tag attribute which defaults to false.

The same technique as above can be used to default to true instead.

EL

The EL syntax enables output outside any taglib so that the previous TLD override technique cannot be used to solve this.

Not known to many developers, EL snippets are governed by so-called EL resolvers. Standard application servers (including servlet containers like Tomcat) provide standard EL resolvers, but it is also possible to add others at runtime.

Though only a single EL resolver can be set in the JSP context, the resolver hierarchy implements the Composite pattern, so it’s not an issue.

Steps required to sanitize EL syntax by default are:

  1. Subclasses relevant necessary EL resolvers - those are ScopedAttributeELResolver, ImplicitObjectELResolver and BeanELResolver, since they may return strings
  2. For each, override the getValue() method:
    • Call super.getValue()
    • Check the return value
    • If it is a string, sanitize the value before returning it, otherwise, leave it as it is
  3. Create a ServletContextListener to register these new EL resolvers
    public class SanitizeELResolverListener implements ServletContextListener {
    
        public void contextInitialized(ServletContextEvent event) {
            ServletContext context = event.getServletContext();
            JspFactory jspFactory = JspFactory.getDefaultFactory();
            JspApplicationContext jspApplicationContext = jspFactory.getJspApplicationContext(context);
            ELResolver sber = new SanitizeBeanELResolver();
            jspApplicationContext.addELResolver(sber);
            // Register other EL resolvers
        }
    }

Summary

Trying to sanitize the output of an application after it has been developed is not the good way to raise developers concerns about security. However, dire situations require dire solutions. When the application has already been developed, the above approaches - one for taglibs, one for EL, show how to achieve this in a way that does not impact existing code and get the job done.

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Sanitizing webapp outputs as an an afterthought
Share this