Introduction
Java Server Pages 2.0 specification introduced a new feature with it support for Expression Language (EL) through the JSP page. This enhancement really made it simple to access data stored in JavaBean components or other java objects such as request, session, application, etc., anywhere in the page. In earlier specifications, this support was restricted to JSTL tags only and required
the use of JSTL tags such as
c:out
, fmt:formatDate
, etc., to access such components, resulting in a more cluttered code. Thanks to JSP 2.0 spec., this clutter is now a history. However this support also resulted in many people using it in an improper manner, making their application susceptible to an attack called XSS attack. This article presents possible solutions to defend against this
attack and also a tool to find out improper usage of EL in JSP pages.
XSS Vulnerability
According to Wikipedia, XSS (Cross Site Scripting) is a type of attack which enables attackers to inject client-side script into
web pages viewed by other users. A cross-site scripting vulnerability may be used by attackers to bypass access controls such as the same origin policy. The effect may range from a petty nuisance to a significant security risk, depending on the sensitivity of the data handled by the vulnerable site and the nature of any security mitigation implemented by the site's owner. There are two major flavors of XSS,
persistent and non-persistent. A very simple use case for persistent XSS attack is described below:
- User A visits a social networking site which is vulnerable to XSS and accesses some page, say a page to add a new post.
- Instead of entering some textual message, user enters an HTML/JavaScript block in this message field and hits
Save. Let's say, a JavaScript block.
- Site stores the message as is.
- User B visits the message page. Upon viewing the message the JavaScript executes and possibly sends the cookie and other headers to a remote site created by User A.
- User A can now use this information to hack session of user B.
JSP EL and XSS Exploit
Let's say that the above site is developed using JSP and the code for outputting the message form looks something like the code shown below:
<%@ page contentType="text/html;charset=utf-8" language="java" buffer="none" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="<a href="http://java.sun.com/jsp/jstl/core">http://java.sun.com/jsp/jstl/core</a>" %>
<%@ taglib prefix="fmt" uri="<a href="http://java.sun.com/jsp/jstl/fmt">http://java.sun.com/jsp/jstl/fmt</a>" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
<meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
<meta http-equiv="Expires" content="0"></meta>
<meta http-equiv="Pragma" content="no-cache"></meta>
<meta http-equiv="Cache-Control" content="no-cache"></meta>
<meta http-equiv="Pragma-directive" content="no-cache"></meta>
<meta http-equiv="cache-directive" content="no-cache"></meta>
<meta name="owner" content="Abra Ka Dabra"></meta>
<meta name="copyright" content="(c) 2020, Future Coding"></meta>
<title>Edit Message Form</title>
<link rel="stylesheet" type="text/css" href="static/css/styles.css"></link>
<link rel="stylesheet" type="text/css" href="static/css/percent-column-system-min.css"></link>
<script type="text/javascript" src="static/js/jquery/jquery-1.9.1.min.js"></script>
</head>
<body>
<div class="pageTitle">Edit Message Form</div>
<div id="frmDiv">
<form name="frmMsg" id="frmMsg" method="post" action="editform.do">
<div>
<label for="txtSubject">Subject :</label><br>
<input type="text" id="txtSubject" name="txtSubject"
size="40" maxlength="80" value="${msgBean.subject}"/>
</div>
<div>
<label for="txtBody">Message :</label><br>
<textarea rows="10" cols="72" id="txtBody"
name="txtBody">${msgBean.body}</textarea>
</div>
<div class="buttonbar">
<button type="button" id="btnPost"
name="btnPost" value="Post" onclick="postMessage()"/>
</div>
<input type="hidden" id="msgId"
name="msgId" value="${msgBean.msgId}"/>
</form>
</div>
</body>
</html>
So what thing in the code causes this attack? There are multiple reasons. The code to save the message saves the message as is without sanitizing it first. However this may or may not be possible if you want to support rich messages. The
retrieved message text is sent as is without sanitizing (HTML Escaping) it first. As seen in the above code the EL is directly embedded in the HTML element. What really happens behind the scenes is that upon encountering the EL the servlet simply evaluates the expression and embeds the result in the output stream.
E.g., in Tomcat, the line which outputs the message body gets translated as shown below:
out.write("\r\n");
out.write("<textarea rows=\"10\" cols=\"72\" id=\"txtBody\" name=\"txtBody\">");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${msgBean.body}", java.lang.String.class,
(PageContext) _jspx_page_context, null, false));
out.write("<textarea>");
The method out.write
does not sanitize (encodes/escapes) the value. As a result browsers see the code as a valid script block and executes the script block. The script block in such
a scenario might be written to gather cookie(s) and hidden field(s) and post this data to a remote site using AJAX,
thus allowing the attacker to gain access to the user's session.
Possible Solutions
There are multiple solutions to tackle this problem and safeguard your site from XSS attacks. These are outlined below:
- Turn off EL support for all pages. This is done by putting the following entries in
web.xml:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>false</scripting-invalid>
</jsp-property-group>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
</jsp-property-group>
</jsp-config>
- Turn on EL support for a specific page. This is done by specifying the
isELIgnored
attribute in the page
directive of the concerned page.
<%@ page isELIgnored ="true|false" %>
- Never use EL expressions directly. Instead use the
c:out
tag to output
values. c:out
by default escapes the outputted value. This results in
the browser rendering the code as HTML text rather than executing it. E.g., the lines outputting the message fields can be re-written as shown below:
<div>
<label for="txtSubject">Subject :</label><br>
<input type="text" id="txtSubject" name="txtSubject" size="40"
maxlength="80" value="<c:out value="${msgBean.subject}"/>"/>
</div>
<div>
<label for="txtBody">Message :</label><br>
<textarea rows="10" cols="72" id="txtBody"
name="txtBody"><c:out value="${msgBean.body}"/></textarea>
</div>
<div class="buttonbar">
<button type="button" id="btnPost" name="btnPost"
value="Post" onclick="postMessage()"/>
</div>
<input type="hidden" id="msgId" name="msgId"
value="<c:out value="${msgBean.msgId}"/>"/>
- If you do not want to use an extra tag then you can use the
escapeXML
JSTL function. The above code in this case can be re-written as shown below:
<div>
<label for="txtSubject">Subject :</label><br>
<input type="text" id="txtSubject" name="txtSubject"
size="40" maxlength="80" value="${fn:escapeXml(msgBean.subject)}"/>
</div>
<div>
<label for="txtBody">Message :</label><br>
<textarea rows="10" cols="72" id="txtBody"
name="txtBody">${fn:escapeXml(msgBean.body)}</textarea>
</div>
<div class="buttonbar">
<button type="button" id="btnPost" name="btnPost"
value="Post" onclick="postMessage()"/>
</div>
<input type="hidden" id="msgId" name="msgId"
value="${fn:escapeXml(msgBean.msgId)}"/>
- Starting with JSP 2.1 it is now possible to register an
ELResolver
, which can escape the EL values. This
ELResolver
is registered by a custom listener, which is configured via
web.xml. The only downside of this approach is that all EL values are escaped and to really output the custom HTML you will have to either use the Scriplet or use JSP
Expression. Yet another way will be to write a custom tag and let it output the unescaped text. More details on this can be found in a nice article written by Chin Huang. The
corresponding code is available in a repository located on GitHub.
The Tool
I have created a small tool based on the Jasper Compiler to detect unsafe EL expression usage across JSP pages. As you might be aware in a Servlet
Container there is no direct mechanism to run JSP. So the servlet container first converts a JSP page into a servlet and then this servlet gets registered automatically to handle the appropriate
URLs. Under Tomcat this translation is done by the Jasper Compiler (org.apache.jasper.compiler.Compiler
). The JSP page parsing is done by the
Parser
class (org.apache.jasper.compiler.Parser
) and finally the servlet code is generated by the
Generator
class (org.apache.jasper.compiler.Generator
). The
Parser
class parses the page and outputs a list containing nodes. Each node is an internal data representation of a JSP page or a JSP
document (XML). The Generator
class uses a Node visitor for traversing the node list in recursive manner and
generates the servlet code. More details on the various types of nodes can be found in
the Jasper Compiler documentation located here.
I have used the Jasper Compiler corresponding to Tomcat version 6.0.36. Although this tool never actually compiles the JSP page it still creates the
Compiler
class instance. This is required as the internal design of the parser requires a reference to
the Compiler
instance. The JspCompilationContext
class serves as a place holder for various things that are used throughout the JSP
Engine. The reason for placing all my classes in the org.apache.jasper.compiler
package is because the
ParserController
, Parser
, Node
, NodeVisitor
classes have a package scope and
are not usable outside this package. The actual validation in this case is performed by
the NodeVisitor
class (org.apache.jasper.compiler.NodeVisitor
). Apart from flagging the unsafe use of EL this tool also detects the use of Scriplets and flags those as errors.
Using the Tool
Being a Java based tool it requires Java JRE 6.0 or above. Like any Java utility the tool is run from the command prompt. Before running the tool make sure that the classpath is set to include all the runtime dependencies mentioned under Tool Dependencies. The
command line for running the tool is
java -cp %CLASS_PATH% org.apache.jasper.compiler.JSPValidator -root <ROOT_DIR> -skipPath
<PATTERNS> -libs <LIB_DIR> -ignoreTags <IGNORE_TAGS> reportFile <REPORT_OUTPUT_FILE>
where
CLASS_PATH
- Represents a list of runtime dependencies (jars) and the tool jar as well, separated by File.pathSeperator
.
E.g., lib/*;build/dist/jspvalidator-1.0.3.jar.ROOT_DIR
- Represents the root folder containing all the jsp, jspf, jspx files. PATTERNS
- Represents a list of Ant like file name or folder name patterns separated by
File.pathSeperator
. LIB_DIR
- Represents the folder containing the additional jars, referenced in the JSP pages either via
the
import
directive or the taglib
directive. IGNORE_TAGS
- A comma separated list of XML qualified tag names.
E.g., "c:out, spring:message"
.REPORT_OUTPUT_FILE
- The full path and name of the file in which scan results are printed.
Tool Dependencies
The tool has the following runtime dependencies other than the Java runtime.
Jar Name | Purpose |
---|
commons-cli-1.2.jar | Command line parsing utility |
commons-lang-2.6.jar | Commons Helper utility for java.lang API |
commons-io-2.4.jar | Commons IO utility |
jasper-6.0.36.jar | Jasper Compiler |
jasper-el-6.0.36.jar | Jasper EL Support |
servlet-api-2.4.jar | The Servlet API reference implementation |
jsp-api-2.1.jar | The JSP API reference implementation |
juli-6.0.36.jar | The logging support |
ecj-4.2.2.jar | The logging support |
History
- March 06, 2013 - Initial release.
- March 29, 2013 - Added support to scan attributes for custom tags, ability to ignore tags,
and ability to ignore files.