Skip to main content

Securing Your Web Application Requests

May 20, 2008

{cs.r.title}






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 "http://www.acegisecurity.org/guide/springsecurity.html#preface">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 "http://www.springframework.org/">Spring Web MVC; Struts 1 and "http://struts.apache.org/2.x/">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:

[prettify]index.jsp?client_id=1824-67-04-F9#4$&amp;client_name=Big Box Company
[/prettify]

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:

[prettify]index.jsp?client_id=1824-67-04-F9%234%24&amp;client_name=Big+Box+Company
[/prettify]

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.

[prettify]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);
}
...    
[/prettify]

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:

[prettify]index.jsp?enc=%3D%3FUTF-8%3FB%3FY2xpZW50X2lkPTE4MjQtNjctMDQtRjklMjM0JTI0%3F%3D&amp;enc=%3D%3FUTF-8%3FB%3FY2xpZW50X25hbWU9QmlnK0JveCtDb21wYW55%3F%3D
[/prettify]

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.

[prettify]/**
 * 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());
    }
  }
}
[/prettify]

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:

[prettify]&lt;bean id="textEncryptor" class="org.jasypt.util.text.StrongTextEncryptor"&gt;
        &lt;property name="password"
        value="servletRequestParameterDecyprtionFilter_password" /&gt;
&lt;/bean&gt;

&lt;bean id="servletRequestParameterDecryptionFilter"
        class="com.spiegssoftware.secureRequestParameters.web.filter.security.ServletRequestParameterDecryptionFilter"&gt;
        &lt;property name="textEncryptor" ref="textEncryptor" /&gt;
&lt;/bean&gt;

&lt;bean id="urlEncryptedParameterTagBean"
        class="com.spiegssoftware.secureRequestParameters.web.tag.security.UrlEncryptedParameterTag"&gt;
        &lt;property name="textEncryptor" ref="textEncryptor" /&gt;
&lt;/bean&gt;
[/prettify]

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.

[prettify]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&lt;String, String&gt; parameterMap = 
     MapSupport.delimitedNameValuesStringToMap(decryptedParametersString, 
       "&amp;", "=");

    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);
}
[/prettify]

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


width="1" height="1" border="0" alt=" " />
Eric Spiegelberg is a Minneapolis-based Java/EE consultant specializing in Spring, Hibernate, and web-based software development.
Related Topics >> Security   |   

Comments

Hi... Thank you for this

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