Securing Your Web Application Requests
Security is a critically important requirement for most software and is something developers invest a great deal of time and energy into designing, developing, testing, and maintaining. The documentation introducing the Spring Security framework (formerly named Acegi) describes application security quite well:
Security is an ever-moving target, and it's important to pursue a comprehensive, system-wide approach. In security circles we encourage you to adopt "layers of security," so that each layer tries to be as secure as possible in its own right, with successive layers providing additional security. The "tighter" the security of each layer, the more robust and safe your application will be.
While incorporating authentication, authorization, and input
validation are no-brainers, most developers overlook a gaping hole:
blindly trusting user input. Just because valid data was sent to
the browser does not mean data returning from the browser hasn't
been modified by the user in some inappropriate fashion. Although
the average user pays no attention, application parameters within
the browser's location bar are plainly visible, and nothing prevents
a user from "customizing" a GET request before it's
sent to the server. By the same reasoning, nothing prevents an
advanced user from locally "customizing" a form and submitting an
altered POST request. In making your application as
secure as possible, it must be able to prevent such
modifications.
A solution to the problem is to secure the application in such a way that recognizing and manipulating sensitive data in generated URLs is not an option. This article presents a design that makes use of a customized JSTL tag to encrypt request parameters and values before they are sent to the browser and a servlet filter to perform decryption upon submission to the server. The result is an easy-to-use and nearly transparent system that adds an additional layer of security to your application.
Although a very brief introduction of JSTL and servlet filters is provided, a general familiarity with both is assumed. Should you need to familiarize yourself with either, please see the Resources section for some recommended introductory articles. No familiarity with cryptographic encryption or decryption algorithms is assumed.
Custom JSTL Tag
Rather than have developers repetitiously writing the common functionality found in nearly all Java-based web applications, Sun released the JSP Standard Tag Library (JSTL). Encouraging developers to move away from the Model 1 style of development with scriptlets, JSTL provides a standard implementation and usage syntax of this common functionality in the form of JSP tag libraries. JSTL has been widely adopted throughout the industry and other model-view-controller (MVC) frameworks, such as Spring Web MVC; Struts 1 and 2, have similar libraries.
One of the commonly used JSTL tags is the
<c:url> tag, which is used to generate URLs
within the application. Out of the box, the
<c:url> tag offers prepending the name of the
application's context path for creating absolute URL references,
session management through URL rewriting, and URL encoding of
request parameters. The <c:url> tag can
optionally accept parameters, which it will use to dynamically
generate the resulting URL.
Because JSTL tags themselves are Java classes, they can be
extended to provide customized or enhanced functionality. In this
particular case, it is the <c:url> tag's ability
to URL encode request parameters we are interested in overriding.
But before discussing how we're going to implement the new
functionality, let's cover conceptually how the
<c:url> tag performs URL encoding. Each request
parameter is composed of a name-value pair, such as:
index.jsp?client_id=1824-67-04-F9#4$&client_name=Big Box Company
To perform URL encoding, the <c:url> tag
takes each individual request parameter value and passes it to a
class that converts the value into the
application/x-www-form-urlencoded MIME format (see the
Java API and URLEncoder for more information). Next,
this updated name-value pair is appended onto a String
containing any previously encoded name-value pairs. The process is
repeated for each name-value pair and after all have been
processed. The aggregated String represents the URL
encoded query String of the request. After encoding,
the previous example is:
index.jsp?client_id=1824-67-04-F9%234%24&client_name=Big+Box+Company
Our customization of the <c:url> tag will
perform a similar process, in which each request parameter undergoes
processing to produce an updated query String, with
the difference being that each request parameter is
cryptographically encrypted instead of being URL encoded. Let's
look at the code.
public class UrlEncryptedParameterTag extends UrlTag
{
...
private TextEncryptor textEncryptor = new BCodecTextEncryptor();
...
/**
* Encrypts and adds a parameter under the name/key contained
* within the encryptedParameterName property.
*
* @param name The parameter name.
* @param value The parameter value.
*/
public void addParameter(String name, String value)
{
if (enabled)
{
logger.debug("Encrypting request parameter: " + name);
StringBuilder sb = new StringBuilder(name);
sb.append("=");
sb.append(value);
String encryptedNameValuePair =
textEncryptor.encrypt(sb.toString());
value = URLEncoder.encode(encryptedNameValuePair,
"UTF-8");
name = "enc";
}
super.addParameter(name, value);
}
...
Because we're modifying how parameters are added to the request,
the above code only needs to override the addParameter(String
name, String value) method of the
<c:url>'s UrlTag class. The
overridden method uses a StringBuilder (a Java 5
enhanced version of the StringBuffer) to concatenate a
request parameter name-value pair, passes this value to a class
that performs encryption, and passes the encrypted value to
super.addParameter(). The resulting encrypted value is
then bound to the HttpRequest under the parameter name
of enc. From there, the tag proceeds as normal. The
previous URL example becomes:
index.jsp?enc=%3D%3FUTF-8%3FB%3FY2xpZW50X2lkPTE4MjQtNjctMDQtRjklMjM0JTI0%3F%3D&enc=%3D%3FUTF-8%3FB%3FY2xpZW50X25hbWU9QmlnK0JveCtDb21wYW55%3F%3D
Note that your application should avoid using the parameter name
enc, as it now indicates an encrypted parameter. While
the chances that this is unacceptable within your application are small, the
parameter name used is configurable.
You can also see from the code that the overridden method does not perform the actual encryption itself and instead delegates to a separate class. This design allows for focused code and easy swapping of encryption algorithms. The next section will discuss the encryption component in closer detail.
Encryption Component
The custom JSTL tag delegates to a separate component the encrypting of the request parameters and values. The first step in creating this component is deciding the cryptographic strength your application requires. Because a large majority of applications manage fairly non-sensitive information, the first code sample will only use Base64 encoding. While not actually considered cryptographically secure, Base64 encoding does convert its input into a non-human-readable format and is, compared to truly cryptographically secure algorithms, quite fast.
/**
* Use Apache Commons Codec's BCodec to provide Base64
* encoding and decoding.
*/
public class BCodecTextEncryptor implements TextEncryptor
{
private BCodec bCodec = new BCodec();
/**
* Encrypt the String.
*
* @param plaintext The input String you wish
* to encrypt.
*/
public String encrypt(String plaintext)
{
try
{
return bCodec.encode(plaintext);
}
catch (EncoderException e)
{
throw new EncryptionOperationNotPossibleException(e.getCause());
}
}
/**
* Decrypt the String.
*
* @param encryptedString The encrypted String
* you wish you decrypt.
*/
public String decrypt(String encryptedString)
{
try
{
return bCodec.decode(encryptedString);
}
catch (DecoderException e)
{
throw new EncryptionOperationNotPossibleException(e.getCause());
}
}
}
Undoubtedly there will be applications that require encryption
of a higher strength. If you look at the above code carefully,
you'll notice that TextEncryptor, the central
interface of the encryption component, is provided by the open
source Jasypt project. An easy-to-use library that provides a number of industrial-strength
cryptographic algorithms without requiring detailed knowledge of
how cryptography works, Jasypt also integrates with numerous open
source projects such as BouncyCastle, Hibernate, Spring, and Spring
Security. Although the sample codes defaults to using Base64
encoding, a setTextEncryptor(TextEncryptor
textEncryptor) method is provided on both the custom JSTL
tag and the decryption filter to allow the algorithm to be changed.
Using Spring's dependency injection, for example, to manage this
configuration, the following example conceptually demonstrates the
required changes to use Jasypt's
StrongTextEncryptor:
<bean id="textEncryptor" class="org.jasypt.util.text.StrongTextEncryptor">
<property name="password"
value="servletRequestParameterDecyprtionFilter_password" />
</bean>
<bean id="servletRequestParameterDecryptionFilter"
class="com.spiegssoftware.secureRequestParameters.web.filter.security.ServletRequestParameterDecryptionFilter">
<property name="textEncryptor" ref="textEncryptor" />
</bean>
<bean id="urlEncryptedParameterTagBean"
class="com.spiegssoftware.secureRequestParameters.web.tag.security.UrlEncryptedParameterTag">
<property name="textEncryptor" ref="textEncryptor" />
</bean>
Notice that no code changes were required to switch encryption algorithms.
With the custom JSTL tag and encryption component in place, we now need a servlet filter to perform the decryption process before the request is handled by the application. We'll discuss this decryption servlet filter next.
Decryption Servlet Filter
A Java servlet filter acts as an interceptor and has the ability to modify or redirect an incoming request or outgoing response. Defined in the web.xml, filters are a flexible way to add powerful functionality to any application. With the JSTL encryption tag from a previous section acting as the first half of the process, a servlet filter is used as the second half of the process and modifies the incoming request to essentially perform the inverse of the custom JSTL tag. Before discussing the details, let's see the code.
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String encryptedParamtersStrings[] =
ServletRequestUtils.getStringParameters(request,
encryptedParameterName);
for (String encryptedParametersString : encryptedParamtersStrings)
{
if (StringUtils.isNotBlank(encryptedParametersString))
{
String decryptedParametersString =
textEncryptor.decrypt(encryptedParametersString);
decryptedParametersString =
URLDecoder.decode(decryptedParametersString, "UTF-8");
Map<String, String> parameterMap =
MapSupport.delimitedNameValuesStringToMap(decryptedParametersString,
"&", "=");
for (String attributeName : parameterMap.keySet())
{
request.setAttribute(attributeName, parameterMap.get(attributeName));
}
}
}
// Remove the encrypted name-value pairs from the request
request.removeAttribute(encryptedParameterName);
// Continue processing of the chain
chain.doFilter(request, response);
}
Because not every parameter received by your application is
encrypted, the filter must be capable of handling both encrypted
and unencrypted parameters. Since we know that the custom JSTL tag
encrypts parameters and binds them to the request under the
parameter name of enc, any parameter in the request
under the this name is decrypted and any parameter under any other
name is left untouched. The servlet filter, similar to the custom
JSTL tag, delegates to the encryption component, which performs the
cryptographic process. This decrypted value will be the original
request parameter name-value pair as it was before being encrypted
by the custom JSTL tag. The servlet filter then binds this
name-value pair to the request under the original parameter name.
The request name-value pair has now been returned to its original
state and, in effect, it is as if no encryption/decryption process
ever took place. From here on, your application will function
normally with no knowledge that an additional layer of security was
added.
Performance
This additional security doesn't come without a cost, of course. By performing encryption in the custom JSTL tag and decryption in the servlet filter, a performance penalty is incurred on each incoming request and outgoing response. Unfortunately, this obviously will reduce the performance and scalability of your application.
If your application can not afford a degradation in performance, the loss can be minimized by making some informed choices. First, evaluate if all request parameters throughout your application require being secured. Chances are not all of them will, and between the two extremes of encrypting everything and encrypting nothing, choose to encrypt only the information that is sensitive or valuable enough to warrant protection. Second, as previously mentioned, evaluate the encryption algorithm used. Once you decide that a particular parameter should be encrypted, is a high-strength algorithm required, or will a computationally lighter algorithm suffice?
Additionally, nothing but a little extra effort prevents you from creating multiple tag-filter pairs, each pair with a varied level of encryption, allowing you to pick and choose a level of encryption appropriate to the sensitivity of what's being encrypted. One tag-filter combination could use a cryptographically insecure but computationally fast algorithm for the majority of the application and another tag-filter combination could use slower-performing but stronger encryption for your most sensitive data. The use of multiple algorithms will improve the overall security by increasing the workload for someone wishing to circumvent the system.
Conclusion
The security of your application is a big deal and your system,
as the old saying goes, is only as strong as its weakest link. By
using a customized JSTL tag, a servlet filter, and a flexible way
to swap encryption algorithms, this article showed how to add an
additional layer of security that transparently encrypts and
decrypts application request parameters and values to prevent users
from altering browser requests. Although we focused on the use of
JSTL, the concept could be applied to other tag libraries such as
those used with Struts 2. Similarly, although the example extended
JSTL's <c:url> tag used for dynamic URL
generation, the concept could also be applied to a form generation
tag and be used to secure form data.
Resources
- Sample code for this article
- Jasypt encryption library
- Spring Security project
- Servlet filter introduction article
- Spring's dependency injection of domain objects documentation
- "JSTL 1.0: Standardizing JSP, Part 1"
- "Writing Servlet 2.3 Filters"
- "Learning Servlet Filters"
- Login or register to post comments
- Printer-friendly version
- 5576 reads




Comments
Hi... Thank you for this
by jagadesan - 2009-11-24 06:44
Hi... Thank you for this wonderful article. I tried the sample given here. It worked well. Here, the parameters (non-editable parameters) are encrypted while the page is loaded using the tag library. but, Is it possible to encrypt editable data (like text box input) on page submit? Thank you Jagadesan