dimanche 29 mai 2011

Valving around with Tomcat


Everybody developer using Tomcat knows (at least a little) about valve.
When playing with server.xml, you can't miss it.

   The critical thing


I've recently built a small valve dealing with a critical vulnerability of JSF 1.1_01 webapps (deployed in Tomcat 5.5.17, 5.5.23).
Yes, it's an old one but still used in many applications.
Your JSF webapp can expose critical informations when running a JSF 1.1_01 web app in a Tomcat container.
Try to access to web.xml file for instance :
http://myhost:myport/myapp/faces/WEB-INF/web.xml
Yes ! It works : Tomcat does not allow such access but JSF 1.1_01 Faces Servlet does.
So, what can we do ?

Valving



Valving is one answer to that question.
A valve is specific mechanism of Catalina, the servlet engine inside Tomcat, it can filter any request or response coming from browser or webapp.
When using a valve you can prevent developers or libs to expose critical contents into browsers.
It acts before any Filter or Servlet deployed in webapps, more, webapps are not reached by the user request if you decide so.
Finally, valve can not be overruled by a webapp configuration or code.
Basically, a valve is a class which implements Valve interface but Catalina API provides a useful Valve base class named ValveBase :
package jee.architect.cookbook.misc.tomcat;
import java.io.IOException;
import java.io.Writer;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.valves.ValveBase;
public class LazyValve extends ValveBase {
    public void invoke(Request request, Response response) throws IOException, javax.servlet.ServletException {

            // Get next Valve and delegate the whole thing
            getNext().invoke(request, response);
    }
}
Get the Maven whole project here.

Securing webapps



We have to check context path in order to allow a request to reach a webapp :
package jee.architect.cookbook.misc.tomcat;
import java.io.IOException;
import java.io.Writer;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.valves.ValveBase;

public class SecurityValve extends ValveBase {

  public void invoke(Request request, Response response) throws IOException, javax.servlet.ServletException {

       String contextPath = request.getRequestURI();

       if (contextPath.contains("/WEB-INF/")
               || contextPath.contains("/META-INF/")) {

           // The response is an error
           response.setError();

           try {
               response.reset();
           } catch (IllegalStateException e) {
               if (container.getLogger().isDebugEnabled()) {
                   container.getLogger().debug(e);               
           }
            
          response.sendError(HttpServletResponse.SC_NOT_FOUND);
            
       } else {
          getNext().invoke(request, response);
       }
   }
}
This version is a bit rude: user get an empty page if they try to access WEB-INF or META-INF paths.
You can get a more user friendly version on my github repository : check here.


Using the valve



Edit server.xml and add the following snippet after engine declaration :
<Valve className="jee.architect.cookbook.misc.tomcat.SecurityValve"/>
Restart Tomcat.


What's next



Valve is powerful mechanism to implement filtering but it useful to implement any behavior relative to request/response processing outside webapps.
For instance : prevent users requests to reach a sensitive webapp when the JVM is under heavy load by pausing requests or sending a wait page to users would be useful.




Contrat Creative Commons

the jee architect cookbook by Olivier SCHMITT est mis à disposition selon les termes de la licence Creative Commons Paternité - Pas d'Utilisation Commerciale - Pas de Modification 3.0 Unported.