Tuesday, November 8, 2011

Security: Seven Security (Mis)Configurations in Java web.xml Files

There are a lot of articles about configuring authentication and authorization in Java web.xml files. Instead of rehashing how to configure roles, protect web resources, and set up different types of authentication let's look at some of the most common security misconfigurations in Java web.xml files.
1) Custom Error Pages Not Configured

By default Java web applications display detailed error messages that disclose the server version and detailed stack trace information that can, in some situations, wind up displaying snippets of Java code. This information is a boon to hackers who are looking for as much information about their victims as possible. Fortunately it's very easy to configure web.xml to display custom error pages. 

Using the following configuration a nice error page will be displayed whenever the application responds with an HTTP 500 error. You can add additional entries for other HTTP status codes as well.

<error-page>
  <error-code>500</error-code>
  <location>/path/to/error.jsp</location>
</error-page>

Additionally, web.xml needs to be configured to prevent detailed stack traces from being displayed by specifying the <exception-type> shown below. Since Throwable is the base class of all Exceptions and Errors in Java, this will ensure that stack traces aren't displayed by the server.

<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/path/to/error.jsp</location>
</error-page>

However, your code can still show stack traces if you do something like this:

<%
try {
  String s = null;
  s.length();
} catch (Exception e) {
  // don't do this!
  e.printStackTrace(new PrintWriter(out));
}
%>

Remember to use appropriate logging in addition to properly configuring your web.xml file.
2) Authentication & Authorization Bypass

The following code shows how to set up web-based access control so that everything in the "secure" directory should only be accessible to users with the "admin" role.


<security-constraint>
  <web-resource-collection>
    <web-resource-name>secure</web-resource-name>
    <url-pattern>/secure/*</url-pattern>
    <http-method>GET</http-method>
    <http-method>POST</http-method>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
</security-constraint>

From a common sense point of view, the <http-method> elements that specify GET and POST should indicate that *only* GET and POST requests are allowed. That is not the case, as any HTTP methods that are *not* explicitly listed are in fact allowed. Arshan Dabirsiaghi has a nice paper that summarizes this issue and shows how to use arbitrary HTTP verbs (like HEAD) and completely fake verbs (like "TEST" or "JUNK") which are not listed in the configuration to bypass web.xml authentication and authorization protections.

Fortunately, the solution is simple. Just remove all <http-method> elements from your web.xml and the configuration will be properly applied to all requests.
3) SSL Not Configured

SSL should be used to protect data in transit in all applications that use sensitive data. You can of course configure SSL on the web server but, once your app server is 
set up with the appropriate SSL keys, it's very easy to enable SSL at the web app level if you so choose.


<security-constraint>
  ...    
  <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
  </user-data-constraint>
</security-constraint>

4) Not Using the Secure Flag

Many web sites use SSL for authentication but then either revert back to non-SSL for subsequent communication or have parts of the site that can still be accessed via non-SSL. This leaves the session coookie (i.e. JSESSIONID) vulnerable to session hijacking attacks. To prevent this, cookies can be created with the "Secure" flag, which ensures that the browser will never transmit the specified cookie over non-SSL.

In older versions of the Servlet specification there wasn't a standard way (although there were vendor specific ways) to define the the JSESSIONID as "Secure". Now in Servlet 3.0 the <cookie-config> element can be used to ensure that the JSESSIONID is not transmitted over plain old HTTP.

<session-config>
  <cookie-config>
    <secure>true</secure>
  </cookie-config>
</session-config>

5) Not Using the HttpOnly Flag

Cookies can be created with the "HttpOnly" flag, which ensures that the cookie cannot be accessed via client side scripts. This helps mitigate some of the most common 
XSS attacks. Just like the "Secure" flag, older versions of the Servlet specification didn't provide a standard way to define the JSESSIONID as "HttpOnly". Now in Servlet 3.0 the element can be configured in web.xml as follows:

<session-config>
  <cookie-config>
    <http-only>true</http-only>
  </cookie-config>
</session-config>

Aside from this new standard approach in Servlet 3.0, older versions of Tomcat allowed the HttpOnly flag to be set with the vendor-specific "useHttpOnly" attribute for the <Context> in server.xml. This attribute was disabled by default in Tomcat 5.5 and 6. But now, in Tomcat 7, the "useHttpOnly" attribute is enabled by default. So, even if you configure web.xml to be <http-only>false</http-only> in Tomcat 7, your JSESSIONID will still be HttpOnly unless you change the default behaviour in server.xml as well.
6) Using URL Parameters for Session Tracking

The <tracking-mode> element in the Servlet 3.0 specification allows you to define whether the JSESSIONID should be stored in a cookie or in a URL parameter. If the session id is stored in a URL parameter it could be inadvertently saved in a number of locations including the browser history, proxy server logs, referrer logs, web logs, etc. Accidental disclosure of the session id makes the application more vulnerable to session hijacking attacks. Instead, make sure the JSESSIONID is stored in a cookie (and has the Secure flag set) using the following configuration:


<session-config>
  <tracking-mode>COOKIE</tracking-mode>
</session-config>

7) Not Setting a Session Timeout

Users like long lived sessions because they are convenient. Hackers like long lived sessions because it gives them more time to conduct attacks like session hijacking and 
CSRF. Security vs usability will always be a dilemma. Once you know how long to keep your session alive you can configure the idle timeout as follows:

<session-config>
  <session-timeout>15</session-timeout>
</session-config>

This examples has the session timing out after 15 minutes of inactivity. If the <session-timeout> element is not configured, the Servlet specification states that the container default should be used (for Tomcat this is 30 minutes). Specifying a number less than or equal to 0 means that the session will never expire. This is definitely not recommended, especially for high assurance applications.

The idle timeout can also be configured by using the setMaxInactiveInterval on the HttpSession class. Unlike the <session-timeout> element, however, this method takes the time in seconds.

In addition to the idle timeout, it would be nice if Java let you configure a hard timeout where the session would be destroyed after some set period of time even if the user was still actively using the application. Something like ASP.NET's slidingExpiration in web.config would be handy in some situations. There's no standard way to configure an absolute timeout but you could implement that logic in a ServletFilter.
Summary

Building and deploying a secure app requires input from many different stakeholders. The environment and configuration are just as important as the code itself. By thinking about some of these 
security misconfigurations ahead of time hopefully you can create more secure and defensible applications. Also, please comment if you have any web.xml tips of your own to share.


No comments :

Post a Comment