From ea0f160ca94c87669f28c92db28f89944d41b544 Mon Sep 17 00:00:00 2001 From: markt Date: Sat, 17 Oct 2009 19:27:12 +0000 Subject: [PATCH] Part 2 of CSRF protection for the host manager. Use POST and require valid nonce. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@826295 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/catalina/manager/host/Constants.java | 13 +- .../manager/host/HTMLHostManagerServlet.java | 133 +++++++++++++++++++-- .../catalina/manager/host/LocalStrings.properties | 4 + 3 files changed, 135 insertions(+), 15 deletions(-) diff --git a/java/org/apache/catalina/manager/host/Constants.java b/java/org/apache/catalina/manager/host/Constants.java index e8e858735..0b11c914a 100644 --- a/java/org/apache/catalina/manager/host/Constants.java +++ b/java/org/apache/catalina/manager/host/Constants.java @@ -27,7 +27,7 @@ public class Constants { "\n" + "\n" + "\n"; + " form {\n" + + " margin: 1;\n" + + " }\n" + + " form.inline {\n" + + " display: inline;\n" + + " }\n" + "\n"; public static final String BODY_HEADER_SECTION = "{0}\n" + @@ -104,7 +109,7 @@ public class Constants { "\n" + "\n" + "\n" + - "\n" + + "
\n" + " \n" + " \n" + "
\n" + " \n" + @@ -119,7 +124,7 @@ public class Constants { "
\n" + "
\n" + - "\n" + + "
\n" + " \n" + " \n" + "\n"; @@ -435,7 +545,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { "\n" + "\n" + " \n" + "
\n" + diff --git a/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java b/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java index 2f9c1cb10..4aeb0d807 100644 --- a/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java +++ b/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java @@ -25,11 +25,13 @@ import java.net.URLEncoder; 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 @@ import org.apache.catalina.util.ServerInfo; 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 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { // 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 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { 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 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { "/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 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { } // 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 @@ public final class HTMLHostManagerServlet extends HostManagerServlet { private static final String HOSTS_ROW_BUTTON_SECTION = " \n" + - " \n" + - "  {1} \n" + - "  {3} \n" + - "  {5} \n" + - " \n" + + "
" + + " " + + "
\n" + + "
" + + " " + + "
\n" + + "
" + + " " + + "
\n" + "
\n" + - "
\n" + + "\n" + + "\n" + "
\n" + diff --git a/java/org/apache/catalina/manager/host/LocalStrings.properties b/java/org/apache/catalina/manager/host/LocalStrings.properties index 926b1f01b..b60ddfbe4 100644 --- a/java/org/apache/catalina/manager/host/LocalStrings.properties +++ b/java/org/apache/catalina/manager/host/LocalStrings.properties @@ -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.serverJVMVendor=JVM Vendor 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 -- 2.11.0