You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2015/04/10 15:51:49 UTC

[2/6] wicket git commit: Cross-Site WebSocket Hijacking protection added. WebsocketSettings extended with allowedDomains. Hijacking protection flag added to websocket settings. WebSocketBehavior and WebSocketResource got new methods called onAbort(). New

Cross-Site WebSocket Hijacking protection added. WebsocketSettings
extended with allowedDomains. Hijacking protection flag added to
websocket settings. WebSocketBehavior and WebSocketResource got new
methods called onAbort(). New broadcast message type: AbortedMessage
introduced. New payload type: WebSocketAbortedPayload introduced.
AbstractWebSocketProcessor closes connection when protection check
fails. WebSocketTesterProcessorTest fixed and cleaned up

Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/5c5c3640
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/5c5c3640
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/5c5c3640

Branch: refs/heads/master
Commit: 5c5c36406a60c96dea9e9c7cf41e46f3733ba257
Parents: 2f3b357
Author: admin <ge...@mail.rakuten.com>
Authored: Fri Mar 20 17:18:56 2015 +0900
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Fri Apr 10 16:51:07 2015 +0300

----------------------------------------------------------------------
 .../wicket/protocol/ws/WebSocketSettings.java   |  64 +++++++++
 .../ws/api/AbstractWebSocketProcessor.java      |  60 ++++++--
 .../ws/api/ConnectionRejectedException.java     |  21 +++
 .../ws/api/IWebSocketConnectionFilter.java      |  38 +++++
 .../protocol/ws/api/WebSocketBehavior.java      |  17 +++
 .../ws/api/WebSocketConnectionOriginFilter.java |  64 +++++++++
 .../protocol/ws/api/WebSocketResource.java      |  18 +++
 .../ws/api/event/WebSocketAbortedPayload.java   |  42 ++++++
 .../protocol/ws/api/message/AbortedMessage.java |  56 ++++++++
 .../ws/util/tester/TestWebSocketProcessor.java  |  14 ++
 .../ws/util/tester/TestWebSocketResource.java   |   8 ++
 .../tester/WebSocketTesterProcessorTest.java    | 144 +++++++++++++++++++
 12 files changed, 531 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
index 3b2d5c9a..92a4a5c 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
@@ -16,6 +16,9 @@
  */
 package org.apache.wicket.protocol.ws;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.Callable;
 
 import org.apache.wicket.Application;
@@ -87,6 +90,16 @@ public class WebSocketSettings
 	private IWebSocketConnectionRegistry connectionRegistry = new SimpleWebSocketConnectionRegistry();
 
 	/**
+	 * The whitelist of allowed domains where the client can connect to the application from
+	 */
+    private final List<String> allowedDomains = new ArrayList<String>();
+
+    /**
+     * Flag which indicates whether connection filtering should be active or not
+     */
+    private boolean protectionNeeded = false;
+
+	/**
 	 * Set the executor for processing websocket push messages broadcasted to all sessions.
 	 * Default executor does all the processing in the caller thread. Using a proper thread pool is adviced
 	 * for applications that send push events from ajax calls to avoid page level deadlocks.
@@ -155,6 +168,57 @@ public class WebSocketSettings
 		return sendPayloadExecutor;
 	}
 
+    /**
+     * Flag that controls whether hijacking protection should be turned on or not
+     *
+     * @param protectionNeeded
+     *            True if protection needed
+     */
+    public void setHijackingProtectionEnabled(boolean protectionNeeded) {
+        this.protectionNeeded = protectionNeeded;
+    }
+
+    /**
+     * Flag that shows whether hijacking protection is turned on or not
+     *
+     * @param protectionNeeded
+     *            True if protection turned on
+     */
+    public boolean isHijackingProtectionEnabled() {
+        return this.protectionNeeded;
+    }
+
+    /**
+     * The list of whitelisted domains which are allowed to initiate a websocket connection. This
+     * list will be eventually used by the
+     * {@link org.apache.wicket.protocol.ws.api.IWebSocketConnectionFilter} to abort potentially
+     * unsafe connections. Example domain names might be:
+     *
+     * <pre>
+     *      http://www.example.com
+     *      http://ww2.example.com
+     * </pre>
+     *
+     * @param domains
+     *            The collection of domains
+     */
+    public void setAllowedDomains(Collection<String> domains) {
+        this.allowedDomains.addAll(domains);
+    }
+
+    /**
+     * The list of whitelisted domains which are allowed to initiate a websocket connection. This
+     * list will be eventually used by the
+     * {@link org.apache.wicket.protocol.ws.api.IWebSocketConnectionFilter} to abort potentially
+     * unsafe connections
+     *
+     * @param domains
+     *            The collection of domains if or an empty list when no domains were added
+     */
+    public List<String> getAllowedDomains() {
+        return this.allowedDomains;
+    }
+
 	/**
 	 * A factory method for the {@link org.apache.wicket.request.http.WebResponse}
 	 * that should be used to write the response back to the client/browser

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
index 8ae53c9..1dac814 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
@@ -29,12 +29,14 @@ import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.http.WicketFilter;
 import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
 import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
 import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
 import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -76,6 +78,20 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 	 */
 	static final int NO_PAGE_ID = -1;
 
+    /**
+     * 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. This is a generic status code
+     * that can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a need to hide specific details about the
+     * policy.
+     * <p>
+     * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
+     */
+    static final int POLICY_VIOLATION = 1008;
+
+    /**
+     * Explanatory text for the client to explain why the connection is getting aborted
+     */
+    static final String ORIGIN_MISMATCH = "Origin mismatch";
+
 	private final WebRequest webRequest;
 	private final int pageId;
 	private final String resourceName;
@@ -84,6 +100,8 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 	private final String sessionId;
 	private final WebSocketSettings webSocketSettings;
 	private final IWebSocketConnectionRegistry connectionRegistry;
+    private final IWebSocketConnectionFilter connectionFilter;
+    private final HttpServletRequest servletRequest;
 
 	/**
 	 * Constructor.
@@ -117,13 +135,17 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		this.baseUrl = Url.parse(baseUrl);
 
 		WicketFilter wicketFilter = application.getWicketFilter();
-		this.webRequest = new WebSocketRequest(new ServletRequestCopy(request), wicketFilter.getFilterPath());
+		this.servletRequest = new ServletRequestCopy(request);
+
+		this.webRequest = new WebSocketRequest(servletRequest, wicketFilter.getFilterPath());
 
 		this.application = Args.notNull(application, "application");
 
 		this.webSocketSettings = WebSocketSettings.Holder.get(application);
 
 		this.connectionRegistry = webSocketSettings.getConnectionRegistry();
+
+        this.connectionFilter = new WebSocketConnectionOriginFilter(webSocketSettings);
 	}
 
 	@Override
@@ -139,20 +161,25 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		broadcastMessage(binaryMessage);
 	}
 
-	/**
-	 * A helper that registers the opened connection in the application-level
-	 * registry.
-	 *
-	 * @param connection
-	 *      the web socket connection to use to communicate with the client
-	 * @see #onOpen(Object)
-	 */
-	protected final void onConnect(final IWebSocketConnection connection)
-	{
-		IKey key = getRegistryKey();
-		connectionRegistry.setConnection(getApplication(), getSessionId(), key, connection);
-		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
-	}
+    /**
+     * A helper that registers the opened connection in the application-level registry.
+     *
+     * @param connection
+     *            the web socket connection to use to communicate with the client
+     * @see #onOpen(Object)
+     */
+    protected final void onConnect(final IWebSocketConnection connection) {
+        IKey key = getRegistryKey();
+        try {
+            connectionRegistry.setConnection(getApplication(), getSessionId(), key, connection);
+            connectionFilter.doFilter(servletRequest);
+            broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
+        } catch (ConnectionRejectedException e) {
+            broadcastMessage(new AbortedMessage(getApplication(), getSessionId(), key));
+            connectionRegistry.removeConnection(getApplication(), getSessionId(), key);
+            connection.close(POLICY_VIOLATION, ORIGIN_MISMATCH);
+        }
+    }
 
 	@Override
 	public void onClose(int closeCode, String message)
@@ -316,6 +343,9 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		{
 			payload = new WebSocketClosedPayload((ClosedMessage) message, handler);
 		}
+		else if (message instanceof AbortedMessage) {
+			payload = new WebSocketAbortedPayload((AbortedMessage) message, handler);
+		}
 		else if (message instanceof IWebSocketPushMessage)
 		{
 			payload = new WebSocketPushPayload((IWebSocketPushMessage) message, handler);

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
new file mode 100644
index 0000000..15af612
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+public class ConnectionRejectedException extends RuntimeException {
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
new file mode 100644
index 0000000..a43c7c1
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Common interface for rejecting connections which are not meeting some of the security concerns.
+ * One example might be when the connection 'Origin' header does not match the origin of the
+ * application host
+ *
+ * @author Gergely Nagy
+ *
+ */
+public interface IWebSocketConnectionFilter {
+
+    /**
+     * Method for rejecting connections based on the current request
+     *
+     * @param servletRequest
+     *            The servlet request holding the request headers
+     */
+    public void doFilter(HttpServletRequest servletRequest);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
index 2f8c67c..803246d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
@@ -18,12 +18,14 @@ package org.apache.wicket.protocol.ws.api;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.event.IEvent;
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
 import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
 import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
 import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -77,6 +79,12 @@ public abstract class WebSocketBehavior extends BaseWebSocketBehavior
 				ClosedMessage message = closedPayload.getMessage();
 				onClose(message);
 			}
+            else if (wsPayload instanceof WebSocketAbortedPayload)
+            {
+                WebSocketAbortedPayload abortedPayload = (WebSocketAbortedPayload) wsPayload;
+                AbortedMessage message = abortedPayload.getMessage();
+                onAbort(message);
+            }
 			else if (wsPayload instanceof WebSocketPushPayload)
 			{
 				WebSocketPushPayload pushPayload = (WebSocketPushPayload) wsPayload;
@@ -121,6 +129,15 @@ public abstract class WebSocketBehavior extends BaseWebSocketBehavior
 	{
 	}
 
+    /**
+     * A callback method called when the server has aborted the connection
+     *
+     * @param message
+     *          the aborted message with the info about the server
+     */
+    protected void onAbort(AbortedMessage message) {
+    }
+
 	/**
 	 * A callback method called when there is a text message sent by the client
 	 *

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
new file mode 100644
index 0000000..655ed30
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+
+public class WebSocketConnectionOriginFilter implements IWebSocketConnectionFilter {
+
+    private final WebSocketSettings webSocketSettings;
+
+    public WebSocketConnectionOriginFilter(WebSocketSettings webSocketSettings) {
+        this.webSocketSettings = webSocketSettings;
+    }
+
+    @Override
+    public void doFilter(HttpServletRequest servletRequest) {
+        if (webSocketSettings.isHijackingProtectionEnabled()) {
+            String oUrl = getOriginUrl(servletRequest);
+            if (invalid(oUrl))
+                throw new ConnectionRejectedException();
+        }
+    }
+
+    private boolean invalid(String oUrl) {
+        if (originMismatch(oUrl))
+            return true;
+        if (oUrl == null || "".equals(oUrl))
+            return true;
+        return false;
+    }
+
+    private boolean originMismatch(String oUrl) {
+        List<String> allowedDomains = webSocketSettings.getAllowedDomains();
+        return !allowedDomains.contains(oUrl);
+    }
+
+    private String getOriginUrl(HttpServletRequest servletRequest) {
+        ArrayList<String> origins = Collections.list(servletRequest.getHeaders("Origin"));
+        if (origins.size() != 1)
+            return null;
+        return origins.get(0);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
index 1db09f9..95aed1d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
@@ -16,12 +16,14 @@
  */
 package org.apache.wicket.protocol.ws.api;
 
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
 import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
 import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
 import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -62,6 +64,12 @@ public abstract class WebSocketResource implements IResource
 			ClosedMessage message = connectedPayload.getMessage();
 			onClose(message);
 		}
+        else if (payload instanceof WebSocketAbortedPayload)
+        {
+            WebSocketAbortedPayload abortedPayload = (WebSocketAbortedPayload) payload;
+            AbortedMessage message = abortedPayload.getMessage();
+            onAbort(message);
+        }
 		else if (payload instanceof WebSocketPushPayload)
 		{
 			WebSocketPushPayload pushPayload = (WebSocketPushPayload) payload;
@@ -94,6 +102,16 @@ public abstract class WebSocketResource implements IResource
 	{
 	}
 
+    /**
+     * A callback method called when the server has aborted the connection
+     *
+     * @param message
+     *          the aborted message with the info about the server
+     */
+    protected void onAbort(AbortedMessage message)
+    {
+    }
+
 	/**
 	 * A callback method called when a WebSocket client has closed the connection
 	 * to the endpoint handled by this WebSocketBehavior

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
new file mode 100644
index 0000000..5a19ad4
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api.event;
+
+import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
+
+/**
+ * * Payload for event broadcasting when the server aborted a WebSocket connection
+ *
+ * @since 7.0.0-M5
+ */
+public class WebSocketAbortedPayload extends WebSocketPayload<AbortedMessage> {
+
+    private final AbortedMessage message;
+
+    public WebSocketAbortedPayload(AbortedMessage message, WebSocketRequestHandler handler) {
+        super(handler);
+
+        this.message = message;
+    }
+
+    @Override
+    public AbortedMessage getMessage() {
+        return message;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
new file mode 100644
index 0000000..2040554
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api.message;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.protocol.ws.api.registry.IKey;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A {@link IWebSocketMessage message} sent when the web socket connection is aborted.
+ *
+ * @since 7.0.0-M5
+ */
+public class AbortedMessage implements IWebSocketMessage {
+
+    private final Application application;
+    private final String sessionId;
+    private final IKey key;
+
+    public AbortedMessage(Application application, String sessionId, IKey key) {
+        this.application = Args.notNull(application, "application");
+        this.sessionId = Args.notNull(sessionId, "sessionId");
+        this.key = Args.notNull(key, "key");
+    }
+
+    public Application getApplication() {
+        return application;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public IKey getKey() {
+        return key;
+    }
+
+    @Override
+    public final String toString() {
+        return "The server aborted the client connection";
+    }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
index 80642a8..1ae8789 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
@@ -36,6 +36,20 @@ import org.apache.wicket.util.tester.WicketTester;
  */
 abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 {
+    /**
+     *
+     * Constructor.
+     *
+     * @param request
+     *            the http request that was used to create the TomcatWebSocketProcessor
+     * @param application
+     *            the current Wicket Application
+     */
+    public TestWebSocketProcessor(final HttpServletRequest request, final WebApplication application)
+    {
+        super(request, application);
+    }
+
 	/**
 	 * Constructor.
 	 *

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
index 2bee3f1..942889d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
@@ -22,6 +22,7 @@ import org.apache.wicket.util.string.Strings;
 import org.junit.Assert;
 import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
 import org.apache.wicket.protocol.ws.api.WebSocketResource;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
 import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
 import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
 import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -37,6 +38,7 @@ public class TestWebSocketResource extends WebSocketResource
 
 	static final AtomicBoolean ON_CONNECT_CALLED = new AtomicBoolean(false);
 	static final AtomicBoolean ON_CLOSE_CALLED = new AtomicBoolean(false);
+	static final AtomicBoolean ON_ABORT_CALLED = new AtomicBoolean(false);
 
 	private final String expectedMessage;
 
@@ -76,6 +78,12 @@ public class TestWebSocketResource extends WebSocketResource
 		super.onClose(message);
 	}
 
+    @Override
+    protected void onAbort(AbortedMessage message) {
+        ON_ABORT_CALLED.set(true);
+        super.onAbort(message);
+    }
+
 	@Override
 	protected void onMessage(WebSocketRequestHandler handler, TextMessage message)
 	{

http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
new file mode 100644
index 0000000..b47b8af
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.util.tester;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.request.http.WebRequest;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class WebSocketTesterProcessorTest extends Assert {
+
+    final static AtomicBoolean messageReceived = new AtomicBoolean(false);
+
+    private static class TestProcessor extends TestWebSocketProcessor {
+        private TestProcessor(HttpServletRequest request, WebApplication application) {
+            super(request, application);
+        }
+
+        @Override
+        protected void onOutMessage(String message) {
+            messageReceived.set(true);
+        }
+
+        @Override
+        protected void onOutMessage(byte[] message, int offset, int length) {
+            messageReceived.set(true);
+        }
+    }
+
+    WicketTester tester;
+    WebApplication application = new MockApplication() {
+        @Override
+        protected void init() {
+            super.init();
+
+            getSharedResources().add(TestWebSocketResource.TEXT, new TestWebSocketResource("expected"));
+        }
+    };
+
+    @Before
+    public void before() {
+        tester = new WicketTester(application);
+        WebApplication webApplication = tester.getApplication();
+        webApplication.getWicketFilter().setFilterPath("");
+    }
+
+    @After
+    public void after() {
+        tester.destroy();
+        TestWebSocketResource.ON_ABORT_CALLED.set(false);
+    }
+
+    @Test
+    public void onConnectNoOrigin() throws Exception {
+        // Given header 'Origin' is missing
+        configureRequest(true, new String[] { "http://www.example.com" }, new String[] {});
+
+        // When we open a connection
+        TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+        processor.onOpen(new Object());
+
+        // Then it fails
+        assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+    }
+
+    @Ignore
+    @Test
+    public void onConnectMultipleOrigins() throws Exception {
+        // Given the request contains multiple header 'Origin's
+        configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://www.example.com", "http://ww2.example.com" });
+
+        // When we open a connection
+        TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+        processor.onOpen(new Object());
+
+        // Then it fails
+        assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+    }
+
+    @Test
+    public void onConnectMatchingOrigin() throws Exception {
+        // Given header 'Origin' matches the host origin
+        configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://www.example.com" });
+
+        // When we open a connection
+        TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+        processor.onOpen(new Object());
+
+        // Then it succeeds
+        assertEquals(false, TestWebSocketResource.ON_ABORT_CALLED.get());
+    }
+
+    @Test
+    public void onConnectMismatchingOrigin() throws Exception {
+        // Given header 'Origin' does not match the host origin
+        configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://ww2.example.com" });
+
+        // When we open a connection
+        TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+        processor.onOpen(new Object());
+
+        // Then it fails
+        assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+    }
+
+    protected void configureRequest(boolean protectionNeeded, String[] allowedDomains, String[] origins) {
+        WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application);
+        webSocketSettings.setHijackingProtectionEnabled(protectionNeeded);
+        webSocketSettings.setAllowedDomains(Arrays.asList(allowedDomains));
+        MockHttpServletRequest request = tester.getRequest();
+        for (String origin : origins) {
+            request.addHeader("Origin", origin);
+        }
+        request.addParameter("resourceName", TestWebSocketResource.TEXT);
+        request.addParameter(WebRequest.PARAM_AJAX_BASE_URL, ".");
+    }
+
+}
\ No newline at end of file