From 1818fc8521fb2d08daa3044f7beee090d92e8ba2 Mon Sep 17 00:00:00 2001 From: Marc Englund Date: Mon, 10 Nov 2008 14:58:17 +0000 Subject: [PATCH] Double cookie submit pattern impl; fixes #2198 svn changeset:5855/svn branch:trunk --- .../gwt/client/ApplicationConnection.java | 9 +++- .../gwt/server/ApplicationServlet.java | 30 +++++++++++++ .../gwt/server/CommunicationManager.java | 45 ++++++++++++------- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java index 827c6353de..b09a9bc08c 100755 --- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java @@ -25,6 +25,7 @@ import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.Cookies; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; @@ -60,6 +61,8 @@ public class ApplicationConnection { public static final String VAR_BURST_SEPARATOR = "\u001d"; + public static final String UIDL_SECURITY_COOKIE_NAME = "com.itmill.toolkit.seckey"; + private final HashMap resourcesMap = new HashMap(); private static Console console; @@ -276,6 +279,10 @@ public class ApplicationConnection { boolean forceSync) { startRequest(); + // cookie double submission pattern + requestData = Cookies.getCookie(UIDL_SECURITY_COOKIE_NAME) + + VAR_BURST_SEPARATOR + requestData; + console.log("Making UIDL Request with params: " + requestData); String uri = getAppUri() + "UIDL" + configuration.getPathInfo(); if (repaintAll) { @@ -637,7 +644,7 @@ public class ApplicationConnection { } if (html.length() != 0) { - INotification n = new INotification(1000 * 60 * 45); // 45min + INotification n = new INotification(1000 * 60 * 45); //45min n.addEventListener(new NotificationRedirect(url)); n.show(html, INotification.CENTERED_TOP, INotification.STYLE_SYSTEM); diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java index e7b61b094b..007843bef9 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java @@ -16,6 +16,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.security.GeneralSecurityException; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; @@ -26,6 +27,7 @@ import java.util.Properties; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,6 +44,7 @@ import com.itmill.toolkit.terminal.ParameterHandler; import com.itmill.toolkit.terminal.Terminal; import com.itmill.toolkit.terminal.ThemeResource; import com.itmill.toolkit.terminal.URIHandler; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; import com.itmill.toolkit.ui.Window; /** @@ -526,6 +529,27 @@ public class ApplicationServlet extends HttpServlet { throw new ServletException(ee); } + } catch (final GeneralSecurityException e) { + // TODO handle differently? + // Invalid security key, show session expired message for now + try { + Application.SystemMessages ci = getSystemMessages(); + if (!UIDLrequest) { + // 'plain' http req - e.g. browser reload; + // just go ahead redirect the browser + response.sendRedirect(ci.getSessionExpiredURL()); + } else { + // send uidl redirect + criticalNotification(request, response, ci + .getSessionExpiredCaption(), ci + .getSessionExpiredMessage(), ci + .getSessionExpiredURL()); + } + request.getSession().invalidate(); + } catch (SystemMessageException ee) { + throw new ServletException(ee); + } + } catch (final Throwable e) { // if this was an UIDL request, response UIDL back to client if (UIDLrequest) { @@ -748,6 +772,12 @@ public class ApplicationServlet extends HttpServlet { HttpServletResponse response, Window window, String themeName, Application application) throws IOException, MalformedURLException { + // Security: double cookie submission pattern + Cookie secCookie = new Cookie( + ApplicationConnection.UIDL_SECURITY_COOKIE_NAME, request + .getSession().getId()); + response.addCookie(secCookie); + // e.g portlets only want a html fragment boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null); if (fragment) { diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java index 0c7da33466..56d4559f22 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java @@ -14,6 +14,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.security.GeneralSecurityException; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -216,7 +217,8 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { */ public void handleUidlRequest(HttpServletRequest request, HttpServletResponse response, ApplicationServlet applicationServlet) - throws IOException, ServletException { + throws IOException, ServletException, + InvalidUIDLSecurityKeyException { // repaint requested or session has timed out and new one is created boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) @@ -572,7 +574,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { */ private boolean handleVariables(HttpServletRequest request, HttpServletResponse response, Application application2, - Window window) throws IOException { + Window window) throws IOException, InvalidUIDLSecurityKeyException { boolean success = true; if (request.getContentLength() > 0) { @@ -591,7 +593,13 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { // Manage bursts one by one final String[] bursts = changes.split(VAR_BURST_SEPARATOR); - for (int bi = 0; bi < bursts.length; bi++) { + // check security key (==sessionid, double cookie submission + if (!request.getSession().getId().equals(bursts[0])) { + throw new InvalidUIDLSecurityKeyException( + "Invalid UIDL security key"); + } + + for (int bi = 1; bi < bursts.length; bi++) { // extract variables to two dim string array final String[] tmp = bursts[bi].split(VAR_RECORD_SEPARATOR); @@ -614,8 +622,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { && variable[VAR_PID] .equals(nextVariable[VAR_PID])) { // we have more than one value changes in row for - // one - // variable owner, collect em in HashMap + // one variable owner, collect em in HashMap m = new HashMap(); m.put(variable[VAR_NAME], convertVariableValue( variable[VAR_TYPE].charAt(0), @@ -645,9 +652,8 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { try { owner.changeVariables(request, m); - // Special-case of closing browser-level - // windows: track browser-windows currently open in - // client + // Special-case of closing browser-level windows: + // track browser-windows currently open in client if (owner instanceof Window && ((Window) owner).getParent() == null) { final Boolean close = (Boolean) m.get("close"); @@ -694,8 +700,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { // not interested in sending any UIDL changes back to client. // Still we must clear component tree between bursts to ensure // that no removed components are updated. The painting after - // the - // last burst is handled normally by the calling method. + // the last burst is handled normally by the calling method. if (bi < bursts.length - 1) { // We will be discarding all changes @@ -1014,8 +1019,8 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { if (logoutUrl == null) { logoutUrl = application.getURL().toString(); } - // clients JS app is still running, send a special json file to - // tell client that application has quit and where to point browser now + // clients JS app is still running, send a special json file to tell + // client that application has quit and where to point browser now // Set the response type response.setContentType("application/json; charset=UTF-8"); final ServletOutputStream out = response.getOutputStream(); @@ -1067,11 +1072,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { final ArrayList resultset = new ArrayList(dirtyPaintabletSet); // The following algorithm removes any components that would be painted - // as - // a direct descendant of other components from the dirty components - // list. - // The result is that each component should be painted exactly once and - // any unmodified components will be painted as "cached=true". + // as a direct descendant of other components from the dirty components + // list. The result is that each component should be painted exactly + // once and any unmodified components will be painted as "cached=true". for (final Iterator i = dirtyPaintabletSet.iterator(); i.hasNext();) { final Paintable p = (Paintable) i.next(); @@ -1299,4 +1302,12 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { return false; } + private class InvalidUIDLSecurityKeyException extends + GeneralSecurityException { + + InvalidUIDLSecurityKeyException(String message) { + super(message); + } + + } } -- 2.39.5