diff options
author | Leif Åstrand <leif@vaadin.com> | 2013-04-25 12:45:45 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-04-25 12:15:48 +0000 |
commit | a46c97bd7936ea8793618bb54ce19ac32c61f71d (patch) | |
tree | d66c1be61070be9620f8107011bd8a372a9e7a72 /server/src | |
parent | 220b1150ca411a63009d7f30e0400dc062f10c27 (diff) | |
download | vaadin-framework-a46c97bd7936ea8793618bb54ce19ac32c61f71d.tar.gz vaadin-framework-a46c97bd7936ea8793618bb54ce19ac32c61f71d.zip |
Verify CSRF token before accepting new CSRF connection (#11635)
* Can't open push connection during client-side init because CSRF token
is not available at that point. This allows simplifying the
initialization because the push state will not be checked until the
first response has been processed.
* Add helper for checking the CSRF token
Change-Id: I31da1ac669dc9a581cbd66f58c07f10ea4b8b676
Diffstat (limited to 'server/src')
6 files changed, 78 insertions, 38 deletions
diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index e83b2dfdef..dddfb385a6 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -526,8 +526,6 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { appConfig.put("heartbeatInterval", vaadinService .getDeploymentConfiguration().getHeartbeatInterval()); - appConfig.put("pushMode", context.getPushMode().toString()); - String serviceUrl = getServiceUrl(context); if (serviceUrl != null) { appConfig.put(ApplicationConstants.SERVICE_URL, serviceUrl); diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 6088fa55ca..11060c6f73 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -52,6 +52,7 @@ import com.vaadin.server.communication.HeartbeatHandler; import com.vaadin.server.communication.PublishedFileHandler; import com.vaadin.server.communication.SessionRequestHandler; import com.vaadin.server.communication.UidlRequestHandler; +import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.JsonConstants; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.ui.UI; @@ -1481,4 +1482,42 @@ public abstract class VaadinService implements Serializable { return false; } + /** + * Verifies that the given CSRF token (aka double submit cookie) is valid + * for the given session. This is used to protect against Cross Site Request + * Forgery attacks. + * <p> + * This protection is enabled by default, but it might need to be disabled + * to allow a certain type of testing. For these cases, the check can be + * disabled by setting the init parameter + * {@value Constants#SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION} to + * <code>true</code>. + * + * @see DeploymentConfiguration#isXsrfProtectionEnabled() + * + * @since 7.1 + * + * @param session + * the vaadin session for which the check should be done + * @param requestToken + * the CSRF token provided in the request + * @return <code>true</code> if the token is valid or if the protection is + * disabled; <code>false</code> if protection is enabled and the + * token is invalid + */ + public static boolean isCsrfTokenValid(VaadinSession session, + String requestToken) { + + if (session.getService().getDeploymentConfiguration() + .isXsrfProtectionEnabled()) { + String keyInSession = (String) session.getSession().getAttribute( + ApplicationConstants.UIDL_SECURITY_TOKEN_ID); + + if (keyInSession == null || !keyInSession.equals(requestToken)) { + return false; + } + } + return true; + } + } diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java index bdc8c723a5..c9d451509c 100644 --- a/server/src/com/vaadin/server/communication/PushHandler.java +++ b/server/src/com/vaadin/server/communication/PushHandler.java @@ -38,6 +38,7 @@ import com.vaadin.server.VaadinServletRequest; import com.vaadin.server.VaadinServletService; import com.vaadin.server.VaadinSession; import com.vaadin.server.WebBrowser; +import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.communication.PushMode; import com.vaadin.ui.UI; @@ -70,15 +71,34 @@ public class PushHandler implements AtmosphereHandler { "New push connection with transport {0}", resource.transport()); resource.getResponse().setContentType("text/plain; charset=UTF-8"); + + VaadinSession session = ui.getSession(); if (resource.transport() == TRANSPORT.STREAMING) { // IE8 requires a longer padding to work properly if the // initial message is small (#11573). Chrome does not work // without the original padding... - WebBrowser browser = ui.getSession().getBrowser(); + WebBrowser browser = session.getBrowser(); if (browser.isIE() && browser.getBrowserMajorVersion() == 8) { resource.padding(LONG_PADDING); } } + + String requestToken = resource.getRequest().getParameter( + ApplicationConstants.CSRF_TOKEN_PARAMETER); + if (!VaadinService.isCsrfTokenValid(session, requestToken)) { + getLogger() + .log(Level.WARNING, + "Invalid CSRF token in new connection received from {0}", + resource.getRequest().getRemoteHost()); + // Refresh on client side, create connection just for + // sending a message + AtmospherePushConnection connection = new AtmospherePushConnection( + ui); + connection.connect(resource); + sendRefresh(connection); + return; + } + resource.suspend(); AtmospherePushConnection connection = new AtmospherePushConnection( @@ -122,19 +142,13 @@ public class PushHandler implements AtmosphereHandler { getLogger().log(Level.SEVERE, "Error writing JSON to response", e); // Refresh on client side - connection - .sendMessage(VaadinService - .createCriticalNotificationJSON(null, null, - null, null)); + sendRefresh(connection); } catch (InvalidUIDLSecurityKeyException e) { getLogger().log(Level.WARNING, "Invalid security key received from {0}", resource.getRequest().getRemoteHost()); // Refresh on client side - connection - .sendMessage(VaadinService - .createCriticalNotificationJSON(null, null, - null, null)); + sendRefresh(connection); } } }; @@ -314,6 +328,11 @@ public class PushHandler implements AtmosphereHandler { public void destroy() { } + private static void sendRefresh(AtmospherePushConnection connection) { + connection.sendMessage(VaadinService.createCriticalNotificationJSON( + null, null, null, null)); + } + private static final Logger getLogger() { return Logger.getLogger(PushHandler.class.getName()); } diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java index 11de224e5e..64f8dfb223 100644 --- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -41,6 +41,7 @@ import com.vaadin.server.ServerRpcManager; import com.vaadin.server.ServerRpcManager.RpcInvocationException; import com.vaadin.server.ServerRpcMethodInvocation; import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; import com.vaadin.server.VariableOwner; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.Connector; @@ -108,29 +109,8 @@ public class ServerRpcHandler implements Serializable { // Security: double cookie submission pattern unless disabled by // property - if (ui.getSession().getConfiguration().isXsrfProtectionEnabled()) { - if (bursts.length == 1 && "init".equals(bursts[0])) { - // init request; don't handle any variables, key sent in - // response. - // TODO This seems to be dead code - request.setAttribute( - LegacyCommunicationManager.WRITE_SECURITY_TOKEN_FLAG, - true); - return; - } else { - // ApplicationServlet has stored the security token in the - // session; check that it matched the one sent in the UIDL - String sessId = (String) ui - .getSession() - .getSession() - .getAttribute( - ApplicationConstants.UIDL_SECURITY_TOKEN_ID); - - if (sessId == null || !sessId.equals(bursts[0])) { - throw new InvalidUIDLSecurityKeyException(""); - } - } - + if (!VaadinService.isCsrfTokenValid(ui.getSession(), bursts[0])) { + throw new InvalidUIDLSecurityKeyException(""); } handleBurst(ui, unescapeBurst(bursts[1])); } diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index efe11e02e9..7c8fc3a0d8 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -37,6 +37,7 @@ import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinSession; +import com.vaadin.shared.communication.PushMode; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.ui.UI; @@ -207,6 +208,13 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { ui.doInit(request, uiId.intValue()); + PushMode pushMode = provider.getPushMode(event); + if (pushMode == null) { + pushMode = session.getService().getDeploymentConfiguration() + .getPushMode(); + } + ui.setPushMode(pushMode); + session.addUI(ui); // Remember if it should be remembered diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 0ad2787cb6..adba9bd83e 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -543,10 +543,6 @@ public abstract class UI extends AbstractSingleComponentContainer implements // Actual theme - used for finding CustomLayout templates theme = request.getParameter("theme"); - PushMode pushMode = PushMode.valueOf(request.getParameter("v-pushMode") - .toUpperCase()); - setPushMode(pushMode); - getPage().init(request); // Call the init overridden by the application developer |