From d17f4afdd039c7f2c34f9ce5bfe2efed22ad72a3 Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 27 Mar 2024 20:33:52 +0200 Subject: [PATCH 01/12] fix IPv6 validation #263 --- src/Ui/UiRequest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index 34af96e2..a61b0439 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -82,7 +82,7 @@ class UiRequest: self.learnHost(host) return True - if ":" in host and helper.isIp(host.rsplit(":", 1)[0]): # Test without port + if ":" in host and helper.isIp(host.rsplit(":", 1)[0].lstrip("[").rstrip("]")): # Test without port self.learnHost(host) return True From 762c5f24029a1e6f034120065889dcbf22841c71 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 15:52:19 +0200 Subject: [PATCH 02/12] remove square brackets from IPv6 host #263 #264 #267 --- src/Ui/UiRequest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index a61b0439..4b11548f 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -544,7 +544,7 @@ class UiRequest: return server_url def getHostWithoutPort(self): - return ':'.join(self.env['HTTP_HOST'].split(':')[:-1]) + return ':'.join(self.env['HTTP_HOST'].split(':')[:-1]).lstrip("[").rstrip("]") def processQueryString(self, site, query_string): match = re.search("zeronet_peers=(.*?)(&|$)", query_string) From 658c685a451a95b83bf1df7988e5a21ed1541e63 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:16:02 +0200 Subject: [PATCH 03/12] add configurable ipv6_testip #263 --- src/File/FileServer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/File/FileServer.py b/src/File/FileServer.py index d1de4761..ee14d4ce 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -98,7 +98,10 @@ class FileServer(ConnectionServer): if config.tor == "always": return True # Test if we can connect to ipv6 address - ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" + if config.ipv6_testip: + ipv6_testip = config.ipv6_testip + else: + ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" try: sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) sock.connect((ipv6_testip, 80)) From 5858a68d1166ee1b1b783ad702d5e9f3dc4ba680 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:22:44 +0200 Subject: [PATCH 04/12] add ipv6_testip to server info #263 --- src/Config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Config.py b/src/Config.py index 8a847bf3..8885f576 100644 --- a/src/Config.py +++ b/src/Config.py @@ -582,6 +582,7 @@ class Config: "fileserver_port": self.fileserver_port, "ui_ip": self.ui_ip, "ui_port": self.ui_port, + "ipv6_testip": self.ipv6_testip, "version": self.version, "rev": self.rev, "language": self.language, From 4030d3a8592219151cf86402ed02a074b22479b1 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:28:58 +0200 Subject: [PATCH 05/12] remove ipv6_testip info as dependent #263 --- src/Config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Config.py b/src/Config.py index 8885f576..8a847bf3 100644 --- a/src/Config.py +++ b/src/Config.py @@ -582,7 +582,6 @@ class Config: "fileserver_port": self.fileserver_port, "ui_ip": self.ui_ip, "ui_port": self.ui_port, - "ipv6_testip": self.ipv6_testip, "version": self.version, "rev": self.rev, "language": self.language, From 8fb98991ef9febe38cc83f8b7ea3b7e7041d6da9 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:31:44 +0200 Subject: [PATCH 06/12] fix optional ipv6_testip check #263 --- src/File/FileServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/File/FileServer.py b/src/File/FileServer.py index ee14d4ce..212cd301 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -98,7 +98,7 @@ class FileServer(ConnectionServer): if config.tor == "always": return True # Test if we can connect to ipv6 address - if config.ipv6_testip: + if hasattr(myObject, "ipv6_testip"): ipv6_testip = config.ipv6_testip else: ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" From 3c24e5d34c6b0101be089be3d5ccde8c142d949c Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:32:30 +0200 Subject: [PATCH 07/12] fix object name #263 --- src/File/FileServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/File/FileServer.py b/src/File/FileServer.py index 212cd301..c74a288d 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -98,7 +98,7 @@ class FileServer(ConnectionServer): if config.tor == "always": return True # Test if we can connect to ipv6 address - if hasattr(myObject, "ipv6_testip"): + if hasattr(config, "ipv6_testip"): ipv6_testip = config.ipv6_testip else: ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" From b928cfc0d5ac8c846045d398075942adf17d63d2 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 16:48:01 +0200 Subject: [PATCH 08/12] add ipv6 support to openBrowser method #263 --- src/util/helper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/helper.py b/src/util/helper.py index af65f727..8c7c6fff 100644 --- a/src/util/helper.py +++ b/src/util/helper.py @@ -325,7 +325,10 @@ def openBrowser(agent): if agent and agent != "False": print(f"Opening browser: {agent}...") ui_ip = config.ui_ip if config.ui_ip != "*" else "127.0.0.1" - url = f'http://{ui_ip}:{config.ui_port}/{config.homepage}' + if ':' in ui_ip: # IPv6 + url = f'http://[{ui_ip}]:{config.ui_port}/{config.homepage}' + else: # IPv4 + url = f'http://{ui_ip}:{config.ui_port}/{config.homepage}' try: import subprocess return subprocess.Popen([config.open_browser, url]) From f4e52fce5a279a89a8cdebad458c21cdb204df86 Mon Sep 17 00:00:00 2001 From: yggverse Date: Thu, 28 Mar 2024 17:06:57 +0200 Subject: [PATCH 09/12] rollback ipv6_testip feature as dependent of config implementation #263 #269 --- src/File/FileServer.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/File/FileServer.py b/src/File/FileServer.py index c74a288d..d1de4761 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -98,10 +98,7 @@ class FileServer(ConnectionServer): if config.tor == "always": return True # Test if we can connect to ipv6 address - if hasattr(config, "ipv6_testip"): - ipv6_testip = config.ipv6_testip - else: - ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" + ipv6_testip = "fcec:ae97:8902:d810:6c92:ec67:efb2:3ec5" try: sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) sock.connect((ipv6_testip, 80)) From 6702f331a3dbdfea6ba463dd370690a6f566c1df Mon Sep 17 00:00:00 2001 From: caryoscelus Date: Sat, 30 Mar 2024 14:31:22 +0000 Subject: [PATCH 10/12] IPv6/non-localhost IP fix - Content-Security-Policy: `frame-src *` for main UiRequest and `self` for user-content UiRequest - revert change in getHostWithoutPort to make ipv6 work --- src/Ui/UiRequest.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index 4b11548f..482096a3 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -382,10 +382,12 @@ class UiRequest: port = int(self.env['SERVER_PORT']) if port == config.ui_port: other_port = config.ui_site_port + frame_src = '*' else: other_port = config.ui_port - site_server = f'{host}:{other_port}' - 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 {site_server}" + frame_src = 'self' + + 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 {frame_src}" if allow_ajax: headers["Access-Control-Allow-Origin"] = "null" @@ -544,7 +546,7 @@ class UiRequest: return server_url def getHostWithoutPort(self): - return ':'.join(self.env['HTTP_HOST'].split(':')[:-1]).lstrip("[").rstrip("]") + return ':'.join(self.env['HTTP_HOST'].split(':')[:-1]) def processQueryString(self, site, query_string): match = re.search("zeronet_peers=(.*?)(&|$)", query_string) From e3b010175f519280adf8f42f01532cd9df5021bd Mon Sep 17 00:00:00 2001 From: caryoscelus Date: Thu, 4 Apr 2024 23:40:32 +0000 Subject: [PATCH 11/12] --ui-ip-protect option to only apply recent privacy protection where it makes sense (i.e. on localhost) by default refs #263, #270 --- src/Config.py | 9 +++++++++ src/Ui/UiRequest.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Config.py b/src/Config.py index 8a847bf3..ba519a6b 100644 --- a/src/Config.py +++ b/src/Config.py @@ -241,6 +241,7 @@ class Config: self.parser.add_argument('--log-rotate-backup-count', help='Log rotate backup count', default=5, type=int) self.parser.add_argument('--language', help='Web interface language', default=language, metavar='language') + self.parser.add_argument('--ui-ip-protect', help="Protect UI server from being accessed through third-party pages and on unauthorized cross-origin pages (enabled by default when serving on localhost IPs; doesn't work with non-local IPs, need testing with host names)", choices=['always', 'local', 'off'], default='local') self.parser.add_argument('--ui-ip', help='Web interface bind address', default="127.0.0.1", metavar='ip') self.parser.add_argument('--ui-port', help='Web interface bind port', default=43110, type=int, metavar='port') self.parser.add_argument('--ui-site-port', help='Port for serving site content, defaults to ui_port+1', default=None, metavar='port') @@ -459,6 +460,14 @@ class Config: self.arguments = self.parser.parse_args(argv[1:]) if self.arguments.ui_site_port is None: self.arguments.ui_site_port = self.arguments.ui_port + 1 + if self.arguments.ui_ip_protect == 'always': + self.arguments.ui_check_cors = True + elif self.arguments.ui_ip_protect == 'off': + self.arguments.ui_check_cors = False + elif self.arguments.ui_ip_protect == 'local': + self.arguments.ui_check_cors = self.arguments.ui_ip == '127.0.0.1' or self.arguments.ui_ip == '::1' + else: + raise Exception("Wrong argparse result") def parseConfig(self, argv): argv = self.fixArgs(argv) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index 482096a3..6e1d5e9e 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -148,7 +148,7 @@ class UiRequest: return False # Deny cross site requests - if not self.isSameOrigin(referer, url) and not self.hasCorsPermission(referer): + if not self.isSameOrigin(referer, url) or not self.hasCorsPermission(referer): return True return False @@ -172,7 +172,7 @@ class UiRequest: protocol = self.env['wsgi.url_scheme'] return self.actionRedirect(f'{protocol}://{host}:{config.ui_port}{path_info}?{query_string}') - if self.isCrossOriginRequest(): + if config.ui_check_cors and self.isCrossOriginRequest(): # 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 [] From 77720365590f5cfde50335c0c251d3a06012b8d5 Mon Sep 17 00:00:00 2001 From: caryoscelus Date: Fri, 5 Apr 2024 10:48:22 +0000 Subject: [PATCH 12/12] Fix some CORS/redirectering cases --- src/Ui/UiRequest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index 6e1d5e9e..70470dba 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -148,7 +148,7 @@ class UiRequest: return False # Deny cross site requests - if not self.isSameOrigin(referer, url) or not self.hasCorsPermission(referer): + if not self.isSameOrigin(referer, url) and not self.hasCorsPermission(referer): return True return False @@ -165,7 +165,7 @@ class UiRequest: 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: + if ((is_navigate and not is_iframe) or not config.ui_check_cors) and self.is_data_request: host = self.getHostWithoutPort() path_info = self.env['PATH_INFO'] query_string = self.env['QUERY_STRING']