Network programming has the sad and calamitous property that the overall security of the affected systems declines exponentially as the convenience and power factors are increased. Incidentally, the various dynamic content generation environments powering the World Wide Web are intended and designed precisely to provide more power to developers and greater convenience for end-users. Security is thus an aspect that must be explicitly factored in by the system architects and developers, and is rarely effective as an afterthought.
The weakest parts of server-side WWW applications, from a security perspective, are inherently the various interaction facilities and afferent channels. These are the immediate means by which adversaries can affect the system, and are invariably subjected to stress during attempts to identify and exploit vulnerabilities. The universal defense strategy against all related attacks is known as input validation.
On a parallel level, security exposures are direct consequences of two main design errors:
- poor access control, and
- implied assumptions about the deployment environment.
There are many extensive treatments of access control issues in the security literature. Here we will discuss the problem of trust management on the low implementation level (code and configuration), as pertaining to the Java Server Pages environment. Accordingly, we will explain the ways in which malicious user input can manifest itself and alter the intended behavior of an application, and we will consider methods to validate input and reduce undesirable exposure of information and programming interfaces.
Brief Overview of JSPThe Java Server Pages technology facilitates the creation and management of dynamic WWW content by embedding Java coded logic inside HTML and XML documents. The pages are preprocessed and converted to Java servlets by the JSP engine. Subsequent requests for the pages result in the Web server responding with the output produced by the corresponding servlets. Although they are functionally equivalent, JSP represents a reversed approach to dynamic content generation as compared to Java servlets in that the focus is on documents with embedded Java code instead of applications with embedded HTML. JSP provides additional HTML-like tags to interact with JavaBeans components for external functionality and access to reusable objects. A noteworthy characteristic of the JSP syntax is that although the HTML syntax is a subset of it (a pure HTML page is a valid JSP page), the reverse is not necessarily true. In particular, JSP allows the embedding of tags within other tags as to facilitate dynamic generation of format as well as content. The following example is a valid JSP construct:
<A HREF = "<%= request.getRemoteUser() %>">
As we will see later, this introduces additional complications from a security point of view.
In comparison to the CGI protocol, JSP offers improved performance and session management (persistent states). This is achieved primarily by using Java threads to handle multiple servlets running inside only one process (which implements a JVM), whereas CGI scripts generally require the creation and destruction of a process for each request.
Security IssuesBy the sheer virtue of providing access to resources on a server, insecure Java servlets derived from JSP pages can put at risk any or all of the server, the network on which this server resides, the clients accessing the pages, and through possible DDoS and worm distribution attacks, the entire Internet. It is often assumed that Java, being the strong-typed, garbage collecting, sandboxable language that it is, magically makes software secure. And indeed, many low-level security issues that are problematic in other languages, such as buffer and heap overflows, are less of a peril in Java. This of course does not mean that writing insecure Java is difficult, especially not when writing servlets. Validating input and controlling access to resources always need to be considered. Furthermore, JSP is a fairly complex architecture, in which many components come together. The interactions between them are often sources of security breaches. And on top of that, although all existing JSP implementations are built around Java, the JSP specification allows virtually any other language to play this role. The security aspects of the substitute language then must also be taken in consideration.
In short, there are plenty of opportunities for introducing vulnerabilities in a JSP system. We will review the most common of them below.
The General Problem of Untrusted User Input
Untrusted user input is, in practice, all user input. It originates from the client but can reach the server through many different channels, and sometimes under disguise. Some source of user input for a JSP server include, but are not limited to:
- the parameter string portion of the request URL,
- data submitted by HTML forms through POST or GET requests,
- data temporarily stored in the client browser (a.k.a. cookies),
- queries to databases,
- environment variables set by other processes.
The problem with user input is that it can be interpreted by the server-side applications and thus an attacker can craft the incoming data so as to control some vulnerable aspect of the server. These vulnerabilities often manifest themselves as points of access to data identified by user-supplied qualifiers, or through execution of external functionality.
Naturally, JSP can make calls to native code stored in libraries (through JNI) and execute external commands. The class Runtime provides the exec() method which interprets its first argument as a command line to execute in a separate process. If parts of this string must be derived from user input, this input must first be filtered to ensure that only the intended commands are executed, with only the intended arguments. Even if the command string does not related to user input in any way, the execution of external commands must still be done with due diligence. It is possible under certain circumstances for an attacker to modify environment variables in the server environment and in this way to affect the execution of external commands, for example by changing the PATH variable to point to a malicious program disguised under the name of the program called by Runtime's exec(). To avoid this risk it is advisable to always set the environment explicitly before making external calls. This can be done by providing an array of environment variables as the second argument to exec(). The variables in this array must have the format name=value.
A similar problem arises when user input is used to identify any kind of input or output stream that the program opens. Access to files, databases, or other network connections must not depend on unvalidated user input. Furthermore, once a stream is open, it rarely safe to directly send user input to it. This is especially true for SQL queries. The following JSP construct accessing the JDBC API is highly insecure, since an attacker can embed command separation characters in the submitted input and thus execute unwanted commands on the SQL server:
<%@ page import="java.sql.*" %> <!-- Some code here to open a SQL connection --> <% Statement stmt = connection.getStatement(); String query = "SELECT * FROM USER_RECORDS WHERE USER = " + request.getParameter("username"); ResultSet result = Statement.executeQuery(query); %>
If username contains a semicolon for instance, as in
the attacker can gain access to (or damage) parts of the database to which they are not entitled (assuming the Web server has privileges to access these parts). In the example attack above, some SQL servers will ignore the whole query, but others will proceed to execute the two commands.
Mitigation of these problems is achieved through appropriate input validation.
Input ValidationInput validation (when the term is used in a security context) consists of performing syntactic and sometimes semantic checks on data derived from external (untrusted) sources, as those listed in the previous section. Depending on the criticality of the application and other factors, the actions performed as a result of input validation may be one or more of the following:
- escaping unsafe syntactic elements,
- replacing syntactic elements with safe ones,
- canceling the use of the affected constructs,
- reporting an error condition,
- activating an IDS.
Input validation can be performed in one of two modes -- rejecting unsafe characters by enumerating them, or rejecting unsafe characters by defining them as the negation of a predefined set of safe characters. The two approaches are known as negative and positive input filtering, respectively. In general, it is simpler and much safer to perform positive input filtering, since it is often a non-trivial task to enumerate all characters that can possibly be misinterpreted by the server-side application, the client browser, the Web server and the operating system on which it executes.
See the section on Cross Site Scripting attacks for an example of input validation meant to protect client browsers from misinterpreting maliciously submitted input.
Sensitive Data in GET Requests and Cookies
The most trivial method for transferring request data from the client to the server-side application is the GET request method, as defined in the CGI protocol. In this method, the input data is appended to the request URL and is represented in the form:
This encoding is clearly unsuitable for transferring security sensitive information, since the full URL and the request string normally travel in clear text over the communication channels and get logged on all intermediate routers as well as on the server. When valuable information needs to be transmitted as part of the client request, the POST method should be used, with a suitable encryption mechanism (e.g. over an SSL connection). From the point of view of the JSP engine, which method is used is largely irrelevant -- both methods are handled identically.
At some point of the development of the World Wide Web, Netscape introduced the concept of a cookie -- a small piece of information the server stores on the client side and later retrieves in order to maintain session state information or to track the actions of the client browser. JSP provides the addCookie() method of the response implicit object to set a cookie on the client side, and the getCookie() method of the request object to retrieve the contents of a cookie. Cookies are instances of the javax.servlet.http.Cookie class. A security exposure is created when sensitive information is stored in cookies, for two reasons. First, the whole content of the cookie is visible to the client, and second, although browsers normally do not provide this capability, there is nothing to prevent a user from responding with an arbitrarily forged cookie.
In general, none of the information submitted by the client browser can be assumed to be safe.
Cross Site ScriptingCERT Advisory CA-2000-02 describes the problem of malicious HTML tags embedded in client Web requests. This is commonly known as "cross site scripting", which is somewhat of a misnomer since it isn't just about scripting, and there is nothing especially cross site about it, but the term has stuck from when the issue was less well understood.
Here is a sample segment for server-side validation of embedded tags:
<!-- HTML code up to here --> <% String message = request.getParameter("message"); message = message.replace ('<','_'); message = message.replace ('>','_'); message = message.replace ('"','_'); message = message.replace ('\'','_'); message = message.replace ('%','_'); message = message.replace (';','_'); message = message.replace ('(','_'); message = message.replace (')','_'); message = message.replace ('&','_'); message = message.replace ('+','_'); %> <p> The message is: <hr/> <tt><%= message %></tt> <hr/> </p> <!-- more HTML below -->
Since it is difficult to enumerate all meta-characters in HTML, the safer approach is to do positive filtering, discarding (or escaping) everything except the explicitly allowed characters (e.g. [A-Za-z0-9]).
A Note on JavaBeansJSP uses a set of conventions described in the JavaBeans specification to access reusable components (Java objects) quickly and conveniently within a JSP page. A Java Bean encapsulates data and functionality that can be used independent of the context in which it is called. A Bean contains data members (properties) and implements a standardized API to access these properties through getter and setter methods.
JSP provides a shorthand notation for initializing all JavaBeans properties of a given Bean by matching name=value pairs in the query string which have the same name as the desired property. Consider the following example of a use of a Bean (here we show the XML syntax):
<jsp:useBean id="myBasket" class="BasketBean"> <jsp:setProperty name="myBasket" property="*"/> <jsp:useBean> <html> <head><title>Your Basket</title></head> <body> <p> You have added the item <jsp::getProperty name="myBasket" property="newItem"/> to your basket. <br/> Your total is $ <jsp::getProperty name="myBasket" property="balance"/> Proceed to <a href="checkout.jsp">checkout</a>
Notice the wild card notation (*) used in the setProperty method call. This instructs JSP to set all properties of the Bean that have been specified in the query string. The script is intended to be used as follows:
which will normally be constructed by an HTML form. The problem of course is that there is nothing to prevent the user from setting the balance property:
When processing the page's <jsp:setProperty> tag, the JSP container will map this parameter to the Bean's like-named balance property, and attempt to set it to $0.
To prevent this, the JSP developer must implement safeguards in the Bean's getter and setter methods (the Bean must enforce access control to its properties), and care must be taken when using the <jsp:setProperty> wild card.
Implementation Vulnerabilities and Source Code DisclosuresCertain versions of every JSP implementation have at some point shipped with exposures that make the system vulnerable, even if the JSP developer follows secure programming practices. In a version of Allaire's JRun for example, if the request URL contained the string .jsp%00 as part of the JSP script extension, the server would not ignore the null byte and will assume that the page is a static non-JSP page to be served as-is. The server will then make a request to the operating system to open the page, at which point the null byte will be ignored and as a result the source of the JSP page will be presented instead of the results of its execution.
Similarly, a version of Tomcat had a vulnerability that would allow attackers to gain access to the JSP source by requesting the page as
The trick here is that %25 is an URL encoded "%'', and 70 is the hexadecimal value for "p''. The Web server does not invoke the JSP handler (since the URL does not end in ".jsp'') but the static file handler manages to map the URL into a correct filename (decoding the URL a second time).
Additionally, many Web servers and JSP implementations come packaged with sample scripts which often contain vulnerabilities. It is safer to disable access to these scripts before the server is deployed in a hostile environment (e.g. the Internet)
In short, JSP programmers must be aware of any current vulnerabilities in the platform for which they are developing. BUGTRAQ and any vendor-specific security announcement lists are a good way too keep informed on such issues.
ConclusionsJSP, like any powerful technology, must be handled with care if secure and reliable operation of the deployed systems is to be assured. In this paper, we provided a concise overview of code and configuration-level security issues that commonly arise in JSP scripts, and offered advice for mitigation of the associated security risks.
About the AuthorJordan Dimov is a consultant for Cigital Inc. in Dulles, Va., and a member of Cigital's Software Security Group.