cmd/geth, cmd/utils, node, rpc: ignore direct ip(v4/6) addresses in rpc virtual hostnames check

This commit is contained in:
Martin Holst Swende 2018-01-25 09:51:18 +01:00
parent 5d0c42e35f
commit 543df3f69c
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
7 changed files with 36 additions and 29 deletions

View file

@ -111,7 +111,7 @@ var (
utils.VMEnableDebugFlag, utils.VMEnableDebugFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.RPCAllowedHostsFlag, utils.RPCVirtualHostsFlag,
utils.EthStatsURLFlag, utils.EthStatsURLFlag,
utils.MetricsEnabledFlag, utils.MetricsEnabledFlag,
utils.FakePoWFlag, utils.FakePoWFlag,

View file

@ -152,7 +152,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.IPCDisabledFlag, utils.IPCDisabledFlag,
utils.IPCPathFlag, utils.IPCPathFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.RPCAllowedHostsFlag, utils.RPCVirtualHostsFlag,
utils.JSpathFlag, utils.JSpathFlag,
utils.ExecFlag, utils.ExecFlag,
utils.PreloadJSFlag, utils.PreloadJSFlag,

View file

@ -383,10 +383,10 @@ var (
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "", Value: "",
} }
RPCAllowedHostsFlag = cli.StringFlag{ RPCVirtualHostsFlag = cli.StringFlag{
Name: "rpcallowedhosts", Name: "rpcvhosts",
Usage: "Comma separated list of hostnames from which to accept requests (server enforced). Set to * to disable this protection.", Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Set to * to disable this protection.",
Value: "localhost,127.0.0.1", Value: "localhost",
} }
RPCApiFlag = cli.StringFlag{ RPCApiFlag = cli.StringFlag{
Name: "rpcapi", Name: "rpcapi",
@ -682,7 +682,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) 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 // setWS creates the WebSocket RPC listener interface string from the set

View file

@ -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 { if hosts != nil {
allowedHosts = nil allowedHosts = nil
for _, host := range strings.Split(*host, ",") { for _, host := range strings.Split(*host, ",") {

View file

@ -105,13 +105,14 @@ type Config struct {
// useless for custom HTTP clients. // useless for custom HTTP clients.
HTTPCors []string `toml:",omitempty"` HTTPCors []string `toml:",omitempty"`
// HTTPHosts is the list of hosts which are allowed on incoming requests. // HTTPVirtualHostnames is the list of virtual hostnames which are allowed on incoming requests.
// This is by default {'localhost','127.0.0.1'}. Using this prevents attacks like // This is by default {'localhost'}. Using this prevents attacks like
// DNS rebinding, which bypasses SOP by simply masquerading as being within the same // 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. // 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 // By explicitly checking the Host-header, the server will not allow requests
// made against the server with a malicious host domain. // 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. // 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 // If the module list is empty, all RPC API endpoints designated public will be

View file

@ -263,7 +263,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
n.stopInProc() n.stopInProc()
return err 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.stopIPC()
n.stopInProc() n.stopInProc()
return err 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) 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 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 // All listeners booted successfully
n.httpEndpoint = endpoint n.httpEndpoint = endpoint
n.httpListener = listener n.httpListener = listener

View file

@ -170,10 +170,6 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
srv.ServeSingleRequest(codec, OptionMethodInvocation) 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 // validateRequest returns a non-zero response code and error message if the
// request is invalid. // request is invalid.
func validateRequest(r *http.Request) (int, error) { func validateRequest(r *http.Request) (int, error) {
@ -220,21 +216,31 @@ type hostHandler struct {
// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
func (h *hostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *hostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Host != "" { // if r.Host is not set, we can continue serving since a browser would set the Host header
hostpart := parseHost(r.Host) if r.Host == "" {
requestAllowed := false h.next.ServeHTTP(w, r)
for _, allowedHost := range h.AllowedHosts { return
if strings.ToLower(allowedHost) == hostpart || allowedHost == "*" { }
requestAllowed = true host, _, err := net.SplitHostPort(r.Host)
break if err != nil {
} // Either invalid (too many colons) or no port specified
} host = r.Host
if !requestAllowed { }
http.Error(w, "invalid host specified", http.StatusForbidden) 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 return
} }
} }
h.next.ServeHTTP(w, r) http.Error(w, "invalid host specified", http.StatusForbidden)
return
} }
func newHostHandler(allowedHosts []string, next http.Handler) http.Handler { func newHostHandler(allowedHosts []string, next http.Handler) http.Handler {