Author: markt
Date: Sat Oct 17 19:27:12 2009
New Revision: 826295

URL: http://svn.apache.org/viewvc?rev=826295&view=rev
Log:
Part 2 of CSRF protection for the host manager. Use POST and require valid 
nonce.

Modified:
    tomcat/trunk/java/org/apache/catalina/manager/host/Constants.java
    
tomcat/trunk/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
    tomcat/trunk/java/org/apache/catalina/manager/host/LocalStrings.properties

Modified: tomcat/trunk/java/org/apache/catalina/manager/host/Constants.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/manager/host/Constants.java?rev=826295&r1=826294&r2=826295&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/manager/host/Constants.java (original)
+++ tomcat/trunk/java/org/apache/catalina/manager/host/Constants.java Sat Oct 
17 19:27:12 2009
@@ -27,7 +27,7 @@
         "<html>\n" +
         "<head>\n" +
         "<style>\n" +
-        org.apache.catalina.util.TomcatCSS.TOMCAT_CSS +
+        org.apache.catalina.util.TomcatCSS.TOMCAT_CSS + "\n" +
         "  table {\n" +
         "    width: 100%;\n" +
         "  }\n" +
@@ -96,7 +96,12 @@
         "    color: black;\n" +
         "    background: white;\n" +
         "  }\n" +
-        "</style>\n";
+        "  form {\n" +
+        "    margin: 1;\n" +
+        "  }\n" +
+        "  form.inline {\n" +
+        "    display: inline;\n" +
+        "  }\n" +        "</style>\n";
 
     public static final String BODY_HEADER_SECTION =
         "<title>{0}</title>\n" +
@@ -104,7 +109,7 @@
         "\n" +
         "<body bgcolor=\"#FFFFFF\">\n" +
         "\n" +
-        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        "<table cellspacing=\"4\" border=\"0\">\n" +
         " <tr>\n" +
         "  <td colspan=\"2\">\n" +
         "   <a href=\"http://www.apache.org/\";>\n" +
@@ -119,7 +124,7 @@
         " </tr>\n" +
         "</table>\n" +
         "<hr size=\"1\" noshade=\"noshade\">\n" +
-        "<table cellspacing=\"4\" width=\"100%\" border=\"0\">\n" +
+        "<table cellspacing=\"4\" border=\"0\">\n" +
         " <tr>\n" +
         "  <td class=\"page-title\" bordercolor=\"#000000\" " +
         "align=\"left\" nowrap>\n" +

Modified: 
tomcat/trunk/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java?rev=826295&r1=826294&r2=826295&view=diff
==============================================================================
--- 
tomcat/trunk/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java 
(original)
+++ 
tomcat/trunk/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java 
Sat Oct 17 19:27:12 2009
@@ -25,11 +25,13 @@
 import java.text.MessageFormat;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Random;
 import java.util.TreeMap;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 
 import org.apache.catalina.Container;
 import org.apache.catalina.Host;
@@ -61,6 +63,14 @@
 
 public final class HTMLHostManagerServlet extends HostManagerServlet {
 
+    private static final long serialVersionUID = 1L;
+
+    protected static final String NONCE_SESSION =
+        "org.apache.catalina.manager.host.NONCE";
+    protected static final String NONCE_REQUEST = "nonce";
+
+    private final Random randomSource = new Random();
+    
     // --------------------------------------------------------- Public Methods
 
     /**
@@ -79,31 +89,120 @@
         // Identify the request parameters that we need
         String command = request.getPathInfo();
 
+        // Prepare our output writer to generate the response message
+        response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+        String message = "";
+        // Process the requested command
+        if (command == null) {
+            // No command == list
+        } else if (command.equals("/list")) {
+            // Nothing to do - always generate list
+        } else if (command.equals("/add") || command.equals("/remove") ||
+                command.equals("/start") || command.equals("/stop")) {
+            message =
+                sm.getString("hostManagerServlet.postCommand", command);
+        } else {
+            message =
+                sm.getString("hostManagerServlet.unknownCommand", command);
+        }
+
+        list(request, response, message);
+    }
+
+    
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse 
response)
+            throws ServletException, IOException {
+        
+        // Identify the request parameters that we need
+        String command = request.getPathInfo();
+
         String name = request.getParameter("name");
+        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 = sm.getString("htmlHostManagerServlet.noNonce", command);
+            // Reset the command
+            command = null;
+        } else {
+            if (!sessionNonce.equals(requestNonce)) {
+                // Nonce mis-match.
+                message =
+                    sm.getString("htmlHostManagerServlet.nonceMismatch", 
command);
+                // Reset the command
+                command = null;
+            }
+        }
+ 
         // Process the requested command
         if (command == null) {
+            // No command == list
         } else if (command.equals("/add")) {
             message = add(request, name);
         } else if (command.equals("/remove")) {
             message = remove(name);
-        } else if (command.equals("/list")) {
         } else if (command.equals("/start")) {
             message = start(name);
         } else if (command.equals("/stop")) {
             message = stop(name);
         } else {
-            message =
-                sm.getString("hostManagerServlet.unknownCommand", command);
+            //Try GET
+            doGet(request, response);
         }
 
         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 String generateNonce() {
+        byte random[] = new byte[16];
+
+        // Render the result as a String of hexadecimal digits
+        StringBuilder buffer = new StringBuilder();
+
+        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();
+    }
+    
     
     /**
      * Add a host using the specified parameters.
@@ -182,6 +281,9 @@
                      HttpServletResponse response,
                      String message) throws IOException {
 
+        String newNonce = generateNonce();
+        request.getSession().setAttribute(NONCE_SESSION, newNonce);
+        
         PrintWriter writer = response.getWriter();
 
         // HTML Header Section
@@ -292,7 +394,7 @@
                      "/html/remove?name=" +
                      URLEncoder.encode(hostName, "UTF-8"));
                 args[5] = hostsRemove;
-                args[6] = RequestUtil.filter(hostName);
+                args[6] = newNonce;
                 if (host == this.host) {
                     writer.print(MessageFormat.format(
                         MANAGER_HOST_ROW_BUTTON_SECTION, args));
@@ -305,13 +407,14 @@
         }
 
         // Add Section
-        args = new Object[6];
+        args = new Object[7];
         args[0] = sm.getString("htmlHostManagerServlet.addTitle");
         args[1] = sm.getString("htmlHostManagerServlet.addHost");
         args[2] = response.encodeURL(request.getContextPath() + "/html/add");
         args[3] = sm.getString("htmlHostManagerServlet.addName");
         args[4] = sm.getString("htmlHostManagerServlet.addAliases");
         args[5] = sm.getString("htmlHostManagerServlet.addAppBase");
+        args[6] = newNonce;
         writer.print(MessageFormat.format(ADD_SECTION_START, args));
  
         args = new Object[3];
@@ -415,11 +518,18 @@
 
     private static final String HOSTS_ROW_BUTTON_SECTION =
         " <td class=\"row-left\" NOWRAP>\n" +
-        "  <small>\n" +
-        "  &nbsp;<a href=\"{0}\" onclick=\"return(confirm(''{1} {6}\\n\\nAre 
you sure?''))\">{1}</a>&nbsp;\n" +
-        "  &nbsp;<a href=\"{2}\" onclick=\"return(confirm(''{3} {6}\\n\\nAre 
you sure?''))\">{3}</a>&nbsp;\n" +
-        "  &nbsp;<a href=\"{4}\" onclick=\"return(confirm(''{5} {6}\\n\\nAre 
you sure?''))\">{5}</a>&nbsp;\n" +
-        "  </small>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{0}\">" +
+        "   <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{6}\"" +
+        "   <small><input type=\"submit\" value=\"{1}\"></small>" +
+        "  </form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{2}\">" +
+        "   <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{6}\"" +
+        "   <small><input type=\"submit\" value=\"{3}\"></small>" +
+        "  </form>\n" +
+        "  <form class=\"inline\" method=\"POST\" action=\"{4}\">" +
+        "   <input type=\"hidden\" name=\"" + NONCE_REQUEST + "\" 
value=\"{6}\"" +
+        "   <small><input type=\"submit\" value=\"{5}\"></small>" +
+        "  </form>\n" +
         " </td>\n" +
         "</tr>\n";
 
@@ -435,7 +545,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=\"{6}\"\n" +
         "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
         "<tr>\n" +
         " <td class=\"row-right\">\n" +

Modified: 
tomcat/trunk/java/org/apache/catalina/manager/host/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/manager/host/LocalStrings.properties?rev=826295&r1=826294&r2=826295&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/manager/host/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/manager/host/LocalStrings.properties 
Sat Oct 17 19:27:12 2009
@@ -14,9 +14,11 @@
 # limitations under the License.
 
 hostManagerServlet.noCommand=FAIL - No command was specified
+hostManagerServlet.postCommand=FAIL - Tried to use command {0} via a GET 
request but POST is required
 hostManagerServlet.unknownCommand=FAIL - Unknown command {0}
 hostManagerServlet.noWrapper=Container has not called setWrapper() for this 
servlet
 hostManagerServlet.invalidHostName=FAIL - Invalid host name {0} was specified
+hostManagerServlet.noHost=FAIL - Host name {0} does not exist
 hostManagerServlet.alreadyHost=FAIL - Host already exists with host name {0}
 hostManagerServlet.managerXml=FAIL - Couldn't install manager.xml
 hostManagerServlet.exception=FAIL - Encountered exception {0}
@@ -68,6 +70,8 @@
 htmlHostManagerServlet.serverOSName=OS Name
 htmlHostManagerServlet.serverOSVersion=OS Version
 htmlHostManagerServlet.serverOSArch=OS Architecture
+htmlHostManagerServlet.noNonce=FAIL: No nonce found in session. Command 
\"{0}\" was ignored
+htmlHostManagerServlet.nonceMismatch=FAIL: Nonce mismatch. Command \"{0}\" was 
ignored.
 
 statusServlet.title=Server Status
 statusServlet.complete=Complete Server Status



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

Reply via email to