Thomas Spura 5cbe496
diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py
Thomas Spura 4893a4d
index 3542346..077dc97 100644
Thomas Spura 5cbe496
--- a/IPython/frontend/html/notebook/handlers.py
Thomas Spura 5cbe496
+++ b/IPython/frontend/html/notebook/handlers.py
Thomas Spura 4893a4d
@@ -16,6 +16,11 @@ Authors:
Thomas Spura 4893a4d
 # Imports
Thomas Spura 4893a4d
 #-----------------------------------------------------------------------------
Thomas Spura 4893a4d
 
Thomas Spura 4893a4d
+try:
Thomas Spura 4893a4d
+    from urllib.parse import urlparse # Py 3
Thomas Spura 4893a4d
+except ImportError:
Thomas Spura 4893a4d
+    from urlparse import urlparse # Py 2
Thomas Spura 4893a4d
+
Thomas Spura 4893a4d
 import logging
Thomas Spura 4893a4d
 import Cookie
Thomas Spura 4893a4d
 import time
Thomas Spura 4893a4d
@@ -369,6 +374,30 @@ class KernelActionHandler(AuthenticatedHandler):
Thomas Spura 5cbe496
 
Thomas Spura 5cbe496
 class ZMQStreamHandler(websocket.WebSocketHandler):
Thomas Spura 5cbe496
 
Thomas Spura 5cbe496
+    def same_origin(self):
Thomas Spura 5cbe496
+        """Check to see that origin and host match in the headers."""
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+        # The difference between version 8 and 13 is that in 8 the
Thomas Spura 5cbe496
+        # client sends a "Sec-Websocket-Origin" header and in 13 it's
Thomas Spura 5cbe496
+        # simply "Origin".
Thomas Spura 5cbe496
+        if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8"):
Thomas Spura 5cbe496
+            origin_header = self.request.headers.get("Sec-Websocket-Origin")
Thomas Spura 5cbe496
+        else:
Thomas Spura 5cbe496
+            origin_header = self.request.headers.get("Origin")
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+        host = self.request.headers.get("Host")
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+        # If no header is provided, assume we can't verify origin
Thomas Spura 5cbe496
+        if(origin_header is None or host is None):
Thomas Spura 5cbe496
+            return False
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+        parsed_origin = urlparse(origin_header)
Thomas Spura 5cbe496
+        origin = parsed_origin.netloc
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+        # Check to see that origin matches host directly, including ports
Thomas Spura 5cbe496
+        return origin == host
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
     def _reserialize_reply(self, msg_list):
Thomas Spura 5cbe496
         """Reserialize a reply message using JSON.
Thomas Spura 5cbe496
 
Thomas Spura 4893a4d
@@ -410,6 +439,11 @@ class ZMQStreamHandler(websocket.WebSocketHandler):
Thomas Spura 5cbe496
 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
Thomas Spura 5cbe496
 
Thomas Spura 5cbe496
     def open(self, kernel_id):
Thomas Spura 5cbe496
+        # Check to see that origin matches host directly, including ports
Thomas Spura 5cbe496
+        if not self.same_origin():
Thomas Spura 5cbe496
+            self.log.warn("Cross Origin WebSocket Attempt.")
Thomas Spura 5cbe496
+            raise web.HTTPError(404)
Thomas Spura 5cbe496
+
Thomas Spura 5cbe496
         self.kernel_id = kernel_id.decode('ascii')
Thomas Spura 5cbe496
         try:
Thomas Spura 5cbe496
             cfg = self.application.ipython_app.config