Browse Source

Allow IP addresses via WebSocket API (#1819)

* Allow IP addresses via WebSocket API

* Switch to socket. Add host
Andrew Morgan 5 years ago
parent
commit
ca549cf081
2 changed files with 41 additions and 4 deletions
  1. 37 2
      src/Ui/UiRequest.py
  2. 4 2
      src/Ui/UiServer.py

+ 37 - 2
src/Ui/UiRequest.py

@@ -4,6 +4,7 @@ import os
 import mimetypes
 import json
 import cgi
+import socket
 
 import gevent
 
@@ -48,10 +49,45 @@ class UiRequest(object):
         self.user = None
         self.script_nonce = None  # Nonce for script tags in wrapper html
 
+    # Test if a string is a valid IP address
+    def isIp(self, host, strip_port=False):
+        if strip_port:
+            # Remove the port from the IP address
+            host = ":".join(host.split(":")[:-1])
+
+        try:
+            # This function will return an exception on a non-valid IP
+            # address
+            socket.inet_aton(host)
+            return True
+
+        except socket.error:
+            # Try for a IPv6 address
+            try:
+                socket.inet_pton(socket.AF_INET6, host)
+                return True
+
+            except socket.error:
+                if not strip_port:
+                    # Try stripping the port and re-checking
+                    return self.isIp(host, strip_port=True)
+
+        return False
+
+    def learnHost(self, host):
+        self.server.allowed_hosts.add(host)
+        self.server.log.info("Added %s as allowed host" % host)
+
     def isHostAllowed(self, host):
         if host in self.server.allowed_hosts:
             return True
 
+        # Allow any IP address as they are not affected by DNS rebinding
+        # attacks
+        if self.isIp(host):
+            self.learnHost(host)
+            return True
+
         if self.isProxyRequest():  # Support for chrome extension proxy
             if self.server.site_manager.isDomain(host):
                 return True
@@ -61,8 +97,7 @@ class UiRequest(object):
         if self.server.learn_allowed_host:
             # Learn the first request's host as allowed one
             self.server.learn_allowed_host = False
-            self.server.allowed_hosts.add(host)
-            self.server.log.info("Added %s as allowed host" % host)
+            self.learnHost(host)
             return True
 
         return False

+ 4 - 2
src/Ui/UiServer.py

@@ -62,7 +62,9 @@ class UiServer:
             self.allowed_hosts = set(config.ui_host)
             self.learn_allowed_host = False
         elif config.ui_ip == "127.0.0.1":
-            self.allowed_hosts = set(["zero", "localhost:%s" % config.ui_port, "127.0.0.1:%s" % config.ui_port])
+            # IP Addresses are inherently allowed as they are immune to DNS
+            # rebinding attacks.
+            self.allowed_hosts = set(["zero", "localhost:%s" % config.ui_port])
             # "URI producers and normalizers should omit the port component and
             # its ':' delimiter if port is empty or if its value would be the
             # same as that of the scheme's default."
@@ -70,7 +72,7 @@ class UiServer:
             # As a result, we need to support portless hosts if port 80 is in
             # use.
             if config.ui_port == 80:
-                self.allowed_hosts.update(["localhost", "127.0.0.1"])
+                self.allowed_hosts.update(["localhost"])
             self.learn_allowed_host = False
         else:
             self.allowed_hosts = set([])