From 543df3f69ce144c4a38f96071dd26a28bdd9e925 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 25 Jan 2018 09:51:18 +0100 Subject: [PATCH] cmd/geth, cmd/utils, node, rpc: ignore direct ip(v4/6) addresses in rpc virtual hostnames check --- cmd/geth/main.go | 2 +- cmd/geth/usage.go | 2 +- cmd/utils/flags.go | 10 +++++----- node/api.go | 2 +- node/config.go | 7 ++++--- node/node.go | 4 ++-- rpc/http.go | 38 ++++++++++++++++++++++---------------- 7 files changed, 36 insertions(+), 29 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 6d690a584e..0f4123dd12 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -111,7 +111,7 @@ var ( utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, - utils.RPCAllowedHostsFlag, + utils.RPCVirtualHostsFlag, utils.EthStatsURLFlag, utils.MetricsEnabledFlag, utils.FakePoWFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index ad00596216..072ac514a4 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -152,7 +152,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.IPCDisabledFlag, utils.IPCPathFlag, utils.RPCCORSDomainFlag, - utils.RPCAllowedHostsFlag, + utils.RPCVirtualHostsFlag, utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a0e5c053f0..b13d203b59 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -383,10 +383,10 @@ var ( Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Value: "", } - RPCAllowedHostsFlag = cli.StringFlag{ - Name: "rpcallowedhosts", - Usage: "Comma separated list of hostnames from which to accept requests (server enforced). Set to * to disable this protection.", - Value: "localhost,127.0.0.1", + RPCVirtualHostsFlag = cli.StringFlag{ + Name: "rpcvhosts", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Set to * to disable this protection.", + Value: "localhost", } RPCApiFlag = cli.StringFlag{ Name: "rpcapi", @@ -682,7 +682,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) } - cfg.HTTPHosts = splitAndTrim(ctx.GlobalString(RPCAllowedHostsFlag.Name)) + cfg.HTTPVirtualHostnames = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name)) } // setWS creates the WebSocket RPC listener interface string from the set diff --git a/node/api.go b/node/api.go index 29a5d95f7a..5f077bb401 100644 --- a/node/api.go +++ b/node/api.go @@ -141,7 +141,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis } } - allowedHosts := api.node.config.HTTPHosts + allowedHosts := api.node.config.HTTPVirtualHostnames if hosts != nil { allowedHosts = nil for _, host := range strings.Split(*host, ",") { diff --git a/node/config.go b/node/config.go index c4f00aaabe..5eba651502 100644 --- a/node/config.go +++ b/node/config.go @@ -105,13 +105,14 @@ type Config struct { // useless for custom HTTP clients. HTTPCors []string `toml:",omitempty"` - // HTTPHosts is the list of hosts which are allowed on incoming requests. - // This is by default {'localhost','127.0.0.1'}. Using this prevents attacks like + // HTTPVirtualHostnames is the list of virtual hostnames which are allowed on incoming requests. + // This is by default {'localhost'}. Using this prevents attacks like // DNS rebinding, which bypasses SOP by simply masquerading as being within the same // origin. These attacks do not utilize CORS, since they are not cross-domain. // By explicitly checking the Host-header, the server will not allow requests // made against the server with a malicious host domain. - HTTPHosts []string `toml:",omitempty"` + // Requests using ip address directly are not affected + HTTPVirtualHostnames []string `toml:",omitempty"` // HTTPModules is a list of API modules to expose via the HTTP RPC interface. // If the module list is empty, all RPC API endpoints designated public will be diff --git a/node/node.go b/node/node.go index aa41eb7cc0..e893356bcd 100644 --- a/node/node.go +++ b/node/node.go @@ -263,7 +263,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { n.stopInProc() return err } - if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPHosts); err != nil { + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHostnames); err != nil { n.stopIPC() n.stopInProc() return err @@ -395,7 +395,7 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors } go rpc.NewHTTPServer(cors, allowedHosts, handler).Serve(listener) n.log.Info(fmt.Sprintf("HTTP endpoint opened: http://%s", endpoint)) - n.log.Info(fmt.Sprintf("HTTP config: cors %v, hosts%v", cors, allowedHosts)) + n.log.Info(fmt.Sprintf("HTTP config: cors %v, vhosts%v", cors, allowedHosts)) // All listeners booted successfully n.httpEndpoint = endpoint n.httpListener = listener diff --git a/rpc/http.go b/rpc/http.go index 047cdf6b37..a3fbb8b7a5 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -170,10 +170,6 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { srv.ServeSingleRequest(codec, OptionMethodInvocation) } -func parseHost(hostport string) string { - return strings.ToLower(strings.Split(hostport, ":")[0]) -} - // validateRequest returns a non-zero response code and error message if the // request is invalid. func validateRequest(r *http.Request) (int, error) { @@ -220,21 +216,31 @@ type hostHandler struct { // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler func (h *hostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Host != "" { - hostpart := parseHost(r.Host) - requestAllowed := false - for _, allowedHost := range h.AllowedHosts { - if strings.ToLower(allowedHost) == hostpart || allowedHost == "*" { - requestAllowed = true - break - } - } - if !requestAllowed { - http.Error(w, "invalid host specified", http.StatusForbidden) + // if r.Host is not set, we can continue serving since a browser would set the Host header + if r.Host == "" { + h.next.ServeHTTP(w, r) + return + } + host, _, err := net.SplitHostPort(r.Host) + if err != nil { + // Either invalid (too many colons) or no port specified + host = r.Host + } + if ipAddr := net.ParseIP(host); ipAddr != nil { + // It's an IP address, we can serve that + h.next.ServeHTTP(w, r) + return + + } + // Not an ip address, but a hostname. Need to validate + for _, allowedHost := range h.AllowedHosts { + if strings.ToLower(allowedHost) == strings.ToLower(host) || allowedHost == "*" { + h.next.ServeHTTP(w, r) return } } - h.next.ServeHTTP(w, r) + http.Error(w, "invalid host specified", http.StatusForbidden) + return } func newHostHandler(allowedHosts []string, next http.Handler) http.Handler {