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:
- Application scoped, with the boolean
defaultHtmlEscape
context parameter<context-param> <param-name>defaultHtmlEscape</param-name> <param-value>true</param-value> </context-param>
- Page scoped (i.e. all forms on page), with the
<spring:defaultHtmlEscape>
tag - 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:
- 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)
)
- Inherit from Spring’s
- Copy the existing Spring TagLib Descriptor and create a new one out of it
- Update it to bind the
message
tag to the newly createdSanitizeMessageTag
class - 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:
- Subclasses relevant necessary EL resolvers - those are
ScopedAttributeELResolver
,ImplicitObjectELResolver
andBeanELResolver
, since they may return strings - 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
- Call
- Create a
ServletContextListener
to register these new EL resolverspublic 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.