From e5b669ce4750d628dba383fd637dbde918ea15f5 2014-01-31 18:34:32 From: Min RK Date: 2014-01-31 18:34:32 Subject: [PATCH] Merge pull request #4845 from rgbkrk/origin_host Add Origin checking for websockets. --- diff --git a/IPython/html/base/zmqhandlers.py b/IPython/html/base/zmqhandlers.py index 0d4c95a..99f432e 100644 --- a/IPython/html/base/zmqhandlers.py +++ b/IPython/html/base/zmqhandlers.py @@ -17,6 +17,11 @@ Authors: #----------------------------------------------------------------------------- try: + from urllib.parse import urlparse # Py 3 +except ImportError: + from urlparse import urlparse # Py 2 + +try: from http.cookies import SimpleCookie # Py 3 except ImportError: from Cookie import SimpleCookie # Py 2 @@ -37,7 +42,30 @@ from .handlers import IPythonHandler #----------------------------------------------------------------------------- class ZMQStreamHandler(websocket.WebSocketHandler): - + + def same_origin(self): + """Check to see that origin and host match in the headers.""" + + # The difference between version 8 and 13 is that in 8 the + # client sends a "Sec-Websocket-Origin" header and in 13 it's + # simply "Origin". + if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8"): + origin_header = self.request.headers.get("Sec-Websocket-Origin") + else: + origin_header = self.request.headers.get("Origin") + + host = self.request.headers.get("Host") + + # If no header is provided, assume we can't verify origin + if(origin_header is None or host is None): + return False + + parsed_origin = urlparse(origin_header) + origin = parsed_origin.netloc + + # Check to see that origin matches host directly, including ports + return origin == host + def clear_cookie(self, *args, **kwargs): """meaningless for websockets""" pass @@ -86,6 +114,11 @@ class ZMQStreamHandler(websocket.WebSocketHandler): class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): def open(self, kernel_id): + # Check to see that origin matches host directly, including ports + if not self.same_origin(): + self.log.warn("Cross Origin WebSocket Attempt.") + raise web.HTTPError(404) + self.kernel_id = cast_unicode(kernel_id, 'ascii') self.session = Session(config=self.config) self.save_on_message = self.on_message @@ -114,4 +147,4 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): if self.get_current_user() is None: self.log.warn("Couldn't authenticate WebSocket connection") raise web.HTTPError(403) - self.on_message = self.save_on_message \ No newline at end of file + self.on_message = self.save_on_message