From a4cc2eeb9fccff2aeece00a5665fea0e906b9075 Mon Sep 17 00:00:00 2001 From: caryoscelus Date: Thu, 7 Dec 2023 07:30:24 +0000 Subject: [PATCH] Separate http server for site content WIP This resolves issue introduced by 14e8130acb4a092b143d0a8046f29926f28e1b25 by having wrapper and iframe exist in different origins Note that this does introduce minor UX issue: copying links now shows them with a different port --- src/Ui/UiRequest.py | 24 +++++++++++++++++++----- src/Ui/UiServer.py | 21 +++++++++++++++------ src/Ui/template/wrapper.html | 4 ++-- src/main.py | 2 +- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index b55fa78d..ced55a00 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -51,11 +51,11 @@ class SecurityError(Exception): @PluginManager.acceptPlugins class UiRequest: - def __init__(self, server, get, env, start_response): + def __init__(self, server, env, start_response, is_data_request=False): if server: self.server = server self.log = server.log - self.get = get # Get parameters + self.get = dict(urllib.parse.parse_qsl(env.get('QUERY_STRING', ''))) self.env = env # Enviroment settings # ['CONTENT_LENGTH', 'CONTENT_TYPE', 'GATEWAY_INTERFACE', 'HTTP_ACCEPT', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_LANGUAGE', # 'HTTP_COOKIE', 'HTTP_CACHE_CONTROL', 'HTTP_HOST', 'HTTP_HTTPS', 'HTTP_ORIGIN', 'HTTP_PROXY_CONNECTION', 'HTTP_REFERER', @@ -66,6 +66,7 @@ class UiRequest: self.start_response = start_response # Start response function self.user = None self.script_nonce = None # Nonce for script tags in wrapper html + self.is_data_request = is_data_request def learnHost(self, host): self.server.allowed_hosts.add(host) @@ -142,8 +143,21 @@ class UiRequest: we'd want something else.. """ + is_navigate = self.env.get('HTTP_SEC_FETCH_MODE') == 'navigate' + is_iframe = self.env.get('HTTP_SEC_FETCH_DEST') == 'iframe' + + if is_navigate and not is_iframe and self.is_data_request: + # remove port from host + host = ':'.join(self.env['HTTP_HOST'].split(':')[:-1]) + path_info = self.env['PATH_INFO'] + query_string = self.env['QUERY_STRING'] + protocol = self.env['wsgi.url_scheme'] + return self.actionRedirect(f'{protocol}://{host}:43110{path_info}?{query_string}') + if self.isCrossOriginRequest(): - return self.error404() + # we are still exposed by answering on port + self.log.warning('Cross-origin request detected. Someone might be trying to analyze your 0net usage') + return [] # Restict Ui access by ip if config.ui_restrict and self.env['REMOTE_ADDR'] not in config.ui_restrict: @@ -341,13 +355,12 @@ class UiRequest: headers["Version"] = "HTTP/1.1" headers["Connection"] = "Keep-Alive" headers["Keep-Alive"] = "max=25, timeout=30" - headers["X-Frame-Options"] = "SAMEORIGIN" headers["Referrer-Policy"] = "same-origin" if noscript: headers["Content-Security-Policy"] = "default-src 'none'; sandbox allow-top-navigation allow-forms; img-src *; font-src * data:; media-src *; style-src * 'unsafe-inline';" elif script_nonce: - headers["Content-Security-Policy"] = "default-src 'none'; script-src 'nonce-{0}'; img-src 'self' blob: data:; style-src 'self' blob: 'unsafe-inline'; connect-src *; frame-src 'self' blob:".format(script_nonce) + headers["Content-Security-Policy"] = f"default-src 'none'; script-src 'nonce-{script_nonce}'; img-src 'self' blob: data:; style-src 'self' blob: 'unsafe-inline'; connect-src *; frame-src 'self' blob: http://127.0.0.1:43111" if allow_ajax: headers["Access-Control-Allow-Origin"] = "null" @@ -618,6 +631,7 @@ class UiRequest: return self.render( "src/Ui/template/wrapper.html", + site_file_server='http://127.0.0.1:43111', server_url=server_url, inner_path=inner_path, file_url=xescape(file_url), diff --git a/src/Ui/UiServer.py b/src/Ui/UiServer.py index 08820832..24fd04d7 100644 --- a/src/Ui/UiServer.py +++ b/src/Ui/UiServer.py @@ -29,7 +29,7 @@ class UiWSGIHandler(WebSocketHandler): import main main.DebugHook.handleError() else: - ui_request = UiRequest(self.server, {}, self.environ, self.start_response) + ui_request = UiRequest(self.server, self.environ, self.start_response, is_data_request=False) block_gen = ui_request.error500("UiWSGIHandler error: %s" % Debug.formatExceptionMessage(err)) for block in block_gen: self.write(block) @@ -96,11 +96,7 @@ class UiServer: # Handle WSGI request def handleRequest(self, env, start_response): path = bytes(env["PATH_INFO"], "raw-unicode-escape").decode("utf8") - if env.get("QUERY_STRING"): - get = dict(urllib.parse.parse_qsl(env['QUERY_STRING'])) - else: - get = {} - ui_request = UiRequest(self, get, env, start_response) + ui_request = UiRequest(self, env, start_response, is_data_request=False) if config.debug: # Let the exception catched by werkezung return ui_request.route(path) else: # Catch and display the error @@ -158,6 +154,19 @@ class UiServer: main.file_server.stop() self.log.debug("Stopped.") + def handleSiteRequest(self, env, start_response): + path = bytes(env["PATH_INFO"], "raw-unicode-escape").decode("utf8") + ui_request = UiRequest(self, env, start_response, is_data_request=True) + try: + return ui_request.route(path) + except Exception as err: + logging.debug(f"UiRequest @ site error: {Debug.formatException(err)}") + return ui_request.error500('Error while trying to server site data') + + def startSiteServer(self): + self.site_server = WSGIServer((self.ip, 43111), self.handleSiteRequest, log=self.log) + self.site_server.serve_forever() + def stop(self): self.log.debug("Stopping...") # Close WS sockets diff --git a/src/Ui/template/wrapper.html b/src/Ui/template/wrapper.html index 2cce69cf..b18a69b1 100644 --- a/src/Ui/template/wrapper.html +++ b/src/Ui/template/wrapper.html @@ -74,11 +74,11 @@ else if (window.opener && window.opener.location.toString()) { - +