Author: markt
Date: Sat Oct 10 23:11:30 2009
New Revision: 823975

URL: http://svn.apache.org/viewvc?rev=823975&view=rev
Log:
Use a nonce to provide CSRF protection

Modified:
    tomcat/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java
    tomcat/trunk/java/org/apache/catalina/manager/LocalStrings.properties

Modified: tomcat/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java?rev=823975&r1=823974&r2=823975&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java Sat 
Oct 10 23:11:30 2009
@@ -30,6 +30,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.TreeMap;
 
 import javax.servlet.ServletContext;
@@ -81,14 +82,22 @@
     protected static final URLEncoder URL_ENCODER; 
     protected static final String APPLICATION_MESSAGE = "message";
     protected static final String APPLICATION_ERROR = "error";
-    protected String sessionsListJspPath  = "/sessionsList.jsp";
-    protected String sessionDetailJspPath = "/sessionDetail.jsp";
+    
+    protected static final String NONCE_SESSION =
+        "org.apache.catalina.manager.NONCE";
+    protected static final String NONCE_REQUEST = "nonce";
+        
+    protected static final String sessionsListJspPath  = "/sessionsList.jsp";
+    protected static final String sessionDetailJspPath = "/sessionDetail.jsp";
 
     static {
         URL_ENCODER = new URLEncoder();
         // '/' should not be encoded in context paths
         URL_ENCODER.addSafeCharacter('/');
     }
+    
+    private final Random randomSource = new Random();
+
     // --------------------------------------------------------- Public Methods
 
     /**
@@ -165,12 +174,30 @@
         String deployPath = request.getParameter("deployPath");
         String deployConfig = request.getParameter("deployConfig");
         String deployWar = request.getParameter("deployWar");
+        String requestNonce = request.getParameter(NONCE_REQUEST);
 
         // Prepare our output writer to generate the response message
         response.setContentType("text/html; charset=" + Constants.CHARSET);
 
         String message = "";
 
+        // Check nonce
+        // There *must* be a nonce in the session before any POST is processed
+        HttpSession session = request.getSession();
+        String sessionNonce = (String) session.getAttribute(NONCE_SESSION);
+        if (sessionNonce == null) {
+            message = "FAIL: No nonce found in session. Command [" + command + 
"] was ignored.";
+            // Reset the command
+            command = null;
+        } else {
+            if (!sessionNonce.equals(requestNonce)) {
+                // Nonce mis-match.
+                message = "FAIL: Nonce mismatch. Command [" + command + "] was 
ignored.";
+                // Reset the command
+                command = null;
+            }
+        }
+        
         if (command == null || command.length() == 0) {
             // No command == list
             // List always displayed -> do nothing
@@ -197,6 +224,36 @@
         list(request, response, message);
     }
 
+    /**
+     * Generate a once time token (nonce) for authenticating subsequent
+     * requests. This will also add the token to the session. The nonce
+     * generation is a simplified version of ManagerBase.generateSessionId().
+     * 
+     */
+    protected synchronized String generateNonce() {
+        byte random[] = new byte[16];
+
+        // Render the result as a String of hexadecimal digits
+        StringBuffer buffer = new StringBuffer();
+
+        randomSource.nextBytes(random);
+       
+        for (int j = 0; j < random.length; j++) {
+            byte b1 = (byte) ((random[j] & 0xf0) >> 4);
+            byte b2 = (byte) (random[j] & 0x0f);
+            if (b1 < 10)
+                buffer.append((char) ('0' + b1));
+            else
+                buffer.append((char) ('A' + (b1 - 10)));
+            if (b2 < 10)
+                buffer.append((char) ('0' + b2));
+            else
+                buffer.append((char) ('A' + (b2 - 10)));
+        }
+
+        return buffer.toString();
+    }
+
     protected String upload(HttpServletRequest request) throws IOException {
         String message = "";
 
@@ -332,6 +389,9 @@
             log("list: Listing contexts for virtual host '" +
                 host.getName() + "'");
 
+        String newNonce = generateNonce();
+        request.getSession().setAttribute(NONCE_SESSION, newNonce);
+        
         PrintWriter writer = response.getWriter();
 
         // HTML Header Section
@@ -457,7 +517,7 @@
                 writer.print
                     (MessageFormat.format(APPS_ROW_DETAILS_SECTION, args));
 
-                args = new Object[14];
+                args = new Object[15];
                 args[0] = response.encodeURL
                     (request.getContextPath() +
                      "/html/start?path=" + URL_ENCODER.encode(displayPath));
@@ -488,9 +548,9 @@
                             context.getManager().getMaxInactiveInterval()/60);
                 }
                 args[12] = sm.getString("htmlManagerServlet.expire.unit");
-                
                 args[13] = highlightColor;
-
+                args[14] = newNonce;
+                
                 if (context.getPath().equals(this.context.getPath())) {
                     writer.print(MessageFormat.format(
                         MANAGER_APP_ROW_BUTTON_SECTION, args));
@@ -512,7 +572,7 @@
         }
 
         // Deploy Section
-        args = new Object[7];
+        args = new Object[8];
         args[0] = sm.getString("htmlManagerServlet.deployTitle");
         args[1] = sm.getString("htmlManagerServlet.deployServer");
         args[2] = response.encodeURL(request.getContextPath() + 
"/html/deploy");
@@ -520,13 +580,15 @@
         args[4] = sm.getString("htmlManagerServlet.deployConfig");
         args[5] = sm.getString("htmlManagerServlet.deployWar");
         args[6] = sm.getString("htmlManagerServlet.deployButton");
+        args[7] = newNonce;
         writer.print(MessageFormat.format(DEPLOY_SECTION, args));
 
-        args = new Object[4];
+        args = new Object[5];
         args[0] = sm.getString("htmlManagerServlet.deployUpload");
         args[1] = response.encodeURL(request.getContextPath() + 
"/html/upload");
         args[2] = sm.getString("htmlManagerServlet.deployUploadFile");
         args[3] = sm.getString("htmlManagerServlet.deployButton");
+        args[4] = newNonce;
         writer.print(MessageFormat.format(UPLOAD_SECTION, args));
 
         // Server Header Section
@@ -1027,6 +1089,7 @@
         " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
         "  <form method=\"POST\" action=\"{8}\">\n" +
         "  <small>\n" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
         "  &nbsp;<input type=\"submit\" value=\"{9}\">&nbsp;{10}&nbsp;<input 
type=\"text\" name=\"idle\" size=\"5\" value=\"{11}\">&nbsp;{12}&nbsp;\n" +
         "  </small>\n" +
         "  </form>\n" +
@@ -1036,14 +1099,24 @@
     private static final String STARTED_DEPLOYED_APPS_ROW_BUTTON_SECTION =
         " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
         "  &nbsp;<small>{1}</small>&nbsp;\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{2}\"><small><input 
type=\"submit\" value=\"{3}\"></small></form>\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{4}\"><small><input 
type=\"submit\" value=\"{5}\"></small></form>\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{6}\"><small><input 
type=\"submit\" value=\"{7}\"></small></form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{2}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{3}\"></small>" +
+        "  </form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{4}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{5}\"></small>" +
+        "  </form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{6}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{7}\"></small>" +
+        "  </form>\n" +
         " </td>\n" +
         " </tr><tr>\n" +
         " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
         "  <form method=\"POST\" action=\"{8}\">\n" +
         "  <small>\n" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
         "  &nbsp;<input type=\"submit\" value=\"{9}\">&nbsp;{10}&nbsp;<input 
type=\"text\" name=\"idle\" size=\"5\" value=\"{11}\">&nbsp;{12}&nbsp;\n" +
         "  </small>\n" +
         "  </form>\n" +
@@ -1052,25 +1125,40 @@
 
     private static final String STOPPED_DEPLOYED_APPS_ROW_BUTTON_SECTION =
         " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{0}\"><small><input 
type=\"submit\" value=\"{1}\"></small></form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{0}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{1}\"></small>" +
+        "  </form>\n" +
         "  &nbsp;<small>{3}</small>&nbsp;\n" +
         "  &nbsp;<small>{5}</small>&nbsp;\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{6}\"><small><input 
type=\"submit\" value=\"{7}\"></small></form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{6}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{7}\"></small>" +
+        "  </form>\n" +
         " </td>\n" +
         "</tr>\n<tr></tr>\n";
 
     private static final String STARTED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION =
         " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
         "  &nbsp;<small>{1}</small>&nbsp;\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{2}\"><small><input 
type=\"submit\" value=\"{3}\"></small></form>\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{4}\"><small><input 
type=\"submit\" value=\"{5}\"></small></form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{2}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{3}\"></small>" +
+        "  </form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{4}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{5}\"></small>" +
+        "  </form>\n" +
         "  &nbsp;<small>{7}</small>&nbsp;\n" +
         " </td>\n" +
         "</tr>\n<tr></tr>\n";
 
     private static final String STOPPED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION =
         " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
-        "  <form class=\"inline\" method=\"POST\" action=\"{0}\"><small><input 
type=\"submit\" value=\"{1}\"></small></form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{0}\">" +
+        "  <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{14}\"" +
+        "  <small><input type=\"submit\" value=\"{1}\"></small>" +
+        "  </form>\n" +
         "  &nbsp;<small>{3}</small>&nbsp;\n" +
         "  &nbsp;<small>{5}</small>&nbsp;\n" +
         "  &nbsp;<small>{7}</small>&nbsp;\n" +
@@ -1089,7 +1177,8 @@
         "</tr>\n" +
         "<tr>\n" +
         " <td colspan=\"2\">\n" +
-        "<form method=\"get\" action=\"{2}\">\n" +
+        "<form method=\"post\" action=\"{2}\">\n" +
+        "<input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" value=\"{7}\"" +
         "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
         "<tr>\n" +
         " <td class=\"row-right\">\n" +
@@ -1134,7 +1223,7 @@
         "</tr>\n" +
         "<tr>\n" +
         " <td colspan=\"2\">\n" +
-        "<form action=\"{1}\" method=\"post\" " +
+        "<form action=\"{1}?" + NONCE_REQUEST + "={4}\" method=\"post\" " +
         "enctype=\"multipart/form-data\">\n" +
         "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
         "<tr>\n" +

Modified: tomcat/trunk/java/org/apache/catalina/manager/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/manager/LocalStrings.properties?rev=823975&r1=823974&r2=823975&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/manager/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/manager/LocalStrings.properties Sat 
Oct 10 23:11:30 2009
@@ -47,6 +47,8 @@
 htmlManagerServlet.manager=Manager
 htmlManagerServlet.messageLabel=Message:
 htmlManagerServlet.noManager=-
+htmlManagerServlet.noNonce=FAIL: No nonce found in session. Command \"{0}\" 
was ignored
+htmlManagerServlet.nonceMismatch=FAIL: Nonce mismatch. Command \"{0}\" was 
ignored.
 htmlManagerServlet.serverJVMVendor=JVM Vendor
 htmlManagerServlet.serverJVMVersion=JVM Version
 htmlManagerServlet.serverOSArch=OS Architecture



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to