49

I found this python script which should allow me to open a WebSocket. However, I receive the warning [W 1402720 14:44:35 web:1811] 403 GET / (192.168.0.102) 11.02 ms in my Linux terminal when trying to open the actual WebSocket (using Old WebSocket Terminal Chrome plugin). The messages "connection opened", "connection closed" and "message received" are never printed in the terminal window.

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket

class MyHandler(tornado.websocket.WebSocketHandler):
        def open(self):
                print "connection opened"
                self.write_message("connection opened")

        def on_close(self):
                print "connection closed"

        def on_message(self,message):
                print "Message received: {}".format(message)
                self.write_message("message received")

if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(handlers=[(r"/",MyHandler)])
        server = tornado.httpserver.HTTPServer(app)
        server.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
Dorus
  • 493
  • 1
  • 4
  • 6
  • I am using Tornado version 4.0 http://www.tornadoweb.org/en/stable/ – Dorus Jul 20 '14 at 14:01
  • In witch way are you trying to open the web-socket connection? – Gabriele Santomaggio Jul 20 '14 at 16:31
  • possible duplicate of [Under tornado v4+ WebSocket connections get refused with 403](http://stackoverflow.com/questions/24800436/under-tornado-v4-websocket-connections-get-refused-with-403) – Ben Darnell Jul 20 '14 at 16:40
  • Thanks Ben, it indeed seems that same problem is discussed there. Must have overlooked, sorry for duplicate post. I now have tornado version 2.4.1 installed and it works like a charm. For now I settle with this and move on. – Dorus Jul 21 '14 at 16:16
  • Gas, I used the chrome extension Simple WebSocket. Found it here: [link](https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo). Currently I am writing a html script to handle this. – Dorus Jul 21 '14 at 16:22

3 Answers3

111

please add

def check_origin(self, origin):
    return True

in class MyHandler like this

class MyHandler(tornado.websocket.WebSocketHandler):

    def check_origin(self, origin):
        return True

    def open(self):
        print "connection opened"
        self.write_message("connection opened")

    def on_close(self):
        print "connection closed"

    def on_message(self,message):
        print "Message received: {}".format(message)
        self.write_message("message received")

From the DOCs:

By default, [check_origin] rejects all requests with an origin on a host other than this one.

This is a security protection against cross site scripting attacks on browsers, since WebSockets are allowed to bypass the usual same-origin policies and don’t use CORS headers.

And again:

This is an important security measure; don’t disable it without understanding the security implications. In particular, if your authentication is cookie-based, you must either restrict the origins allowed by check_origin() or implement your own XSRF-like protection for websocket connections. See these articles for more.

Link.

Aurelio
  • 24,702
  • 9
  • 60
  • 63
maxhawkdown
  • 1,126
  • 1
  • 8
  • 2
5

Don't just set return True on check_origin() because it's a security threat, use a list of allowed domains instead, i.e.:

def check_origin(self, origin):
    allowed = ["https://site1.tld", "https://site2.tld"]
    if origin in allowed:
        print("allowed", origin)
        return 1
Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
1

Slightly modified @maxhawkdown's solution.

from tornado.util import PY3

if PY3:
    from urllib.parse import urlparse  # py2

    xrange = range
else:
    from urlparse import urlparse  # py3


class ChatHandler(tornado.websocket.WebSocketHandler):
    CORS_ORIGINS = ['localhost']

    def check_origin(self, origin):
        parsed_origin = urlparse(origin)
        # parsed_origin.netloc.lower() gives localhost:3333
        return parsed_origin.hostname in self.CORS_ORIGINS
guneysus
  • 6,203
  • 2
  • 45
  • 47