commit 713ae1e8f19a39507c33d444bb99e9414c8bbe99
parent b4ca604518f3656ac73f54b2513e6812fee206a0
Author: Sheng <webmaster0115@gmail.com>
Date: Sat, 19 Jan 2019 19:19:45 +0800
Support cross origin connect
Diffstat:
3 files changed, 87 insertions(+), 22 deletions(-)
diff --git a/tests/test_app.py b/tests/test_app.py
@@ -449,6 +449,7 @@ class OtherTestBase(AsyncHTTPTestCase):
syshostfile = ''
tdstream = ''
maxconn = 20
+ origin = 'same'
body = {
'hostname': '127.0.0.1',
'port': '',
@@ -467,6 +468,7 @@ class OtherTestBase(AsyncHTTPTestCase):
options.syshostfile = self.syshostfile
options.tdstream = self.tdstream
options.maxconn = self.maxconn
+ options.origin = self.origin
app = make_app(make_handlers(loop, options), get_app_settings(options))
return app
@@ -716,3 +718,28 @@ class TestAppWithTooManyConnections(OtherTestBase):
self.assertEqual(data['status'], 'Too many connections.')
ws.close()
+
+
+class TestAppWithCrossOriginConnect(OtherTestBase):
+
+ origin = 'http://www.example.com'
+
+ @tornado.testing.gen_test
+ def test_app_with_cross_orgin_connect(self):
+ url = self.get_url('/')
+ client = self.get_http_client()
+ body = urlencode(dict(self.body, username='foo', _origin='localhost'))
+ response = yield client.fetch(url, method='POST', body=body,
+ headers=self.headers)
+ data = json.loads(to_str(response.body))
+ self.assertIsNone(data['id'])
+ self.assertIsNone(data['encoding'])
+ self.assertIn('Cross origin frame', data['status'])
+
+ body = urlencode(dict(self.body, username='foo', _origin=self.origin))
+ response = yield client.fetch(url, method='POST', body=body,
+ headers=self.headers)
+ data = json.loads(to_str(response.body))
+ self.assertIsNotNone(data['id'])
+ self.assertIsNotNone(data['encoding'])
+ self.assertIsNone(data['status'])
diff --git a/webssh/handler.py b/webssh/handler.py
@@ -76,6 +76,28 @@ class MixinHandler(object):
else:
self.context = context
+ def check_origin(self, origin):
+ if self.origin_policy == '*':
+ return True
+
+ parsed_origin = urlparse(origin)
+ netloc = parsed_origin.netloc.lower()
+ logging.debug('netloc: {}'.format(netloc))
+
+ host = self.request.headers.get('Host')
+ logging.debug('host: {}'.format(host))
+
+ if netloc == host:
+ return True
+
+ if self.origin_policy == 'same':
+ return False
+ elif self.origin_policy == 'primary':
+ return is_same_primary_domain(netloc.rsplit(':', 1)[0],
+ host.rsplit(':', 1)[0])
+ else:
+ return origin in self.origin_policy
+
def is_forbidden(self, context, hostname):
ip = context.address[0]
lst = context.trusted_downstream
@@ -340,6 +362,13 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler):
if len(clients.get(self.src_addr[0], {})) >= options.maxconn:
raise tornado.web.HTTPError(403, 'Too many connections.')
+ origin = self.get_argument('_origin', u'')
+ if origin:
+ if not self.check_origin(origin):
+ raise tornado.web.HTTPError(
+ 403, 'Cross origin frame operation is not allowed.'
+ )
+
future = Future()
t = threading.Thread(target=self.ssh_connect_wrapped, args=(future,))
t.setDaemon(True)
@@ -364,28 +393,6 @@ class WsockHandler(MixinHandler, tornado.websocket.WebSocketHandler):
super(WsockHandler, self).initialize(loop)
self.worker_ref = None
- def check_origin(self, origin):
- if self.origin_policy == '*':
- return True
-
- parsed_origin = urlparse(origin)
- netloc = parsed_origin.netloc.lower()
- logging.debug('netloc: {}'.format(netloc))
-
- host = self.request.headers.get('Host')
- logging.debug('host: {}'.format(host))
-
- if netloc == host:
- return True
-
- if self.origin_policy == 'same':
- return False
- elif self.origin_policy == 'primary':
- return is_same_primary_domain(netloc.rsplit(':', 1)[0],
- host.rsplit(':', 1)[0])
- else:
- return origin in self.origin_policy
-
def open(self):
self.src_addr = self.get_client_addr()
logging.info('Connected from {}:{}'.format(*self.src_addr))
diff --git a/webssh/static/js/main.js b/webssh/static/js/main.js
@@ -50,6 +50,7 @@ jQuery(function($){
messages = {1: 'This client is connecting ...', 2: 'This client is already connnected.'},
key_max_size = 16384,
fields = ['hostname', 'port', 'username'],
+ event_origin,
hostname_tester = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/;
@@ -517,6 +518,9 @@ jQuery(function($){
}
data._xsrf = _xsrf.value;
+ if (event_origin) {
+ data._origin = event_origin;
+ }
$.ajax({
url: url,
@@ -569,4 +573,31 @@ jQuery(function($){
connect();
});
+
+ function cross_origin_connect(event)
+ {
+ console.log(event.origin);
+ var prop = 'connect',
+ args;
+
+ try {
+ args = JSON.parse(event.data);
+ } catch (SyntaxError) {
+ args = event.data.split('|');
+ }
+
+ if (!Array.isArray(args)) {
+ args = [args];
+ }
+
+ try {
+ event_origin = event.origin;
+ wssh[prop].apply(wssh, args);
+ } finally {
+ event_origin = undefined;
+ }
+ }
+
+ window.addEventListener('message', cross_origin_connect, false);
+
});