From 589b603a9b1e17930d1e83ca64ce7cdc4c3d5c85 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende <martin@swende.se>
Date: Mon, 12 Feb 2018 13:52:07 +0100
Subject: rpc: dns rebind protection (#15962)

* cmd,node,rpc: add allowedHosts to prevent dns rebinding attacks

* p2p,node: Fix bug with dumpconfig introduced in r54aeb8e4c0bb9f0e7a6c67258af67df3b266af3d

* rpc: add wildcard support for rpcallowedhosts + go fmt

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

* http, rpc, utils: make vhosts into map, address review concerns

* node: change log messages to use geth standard (not sprintf)

* rpc: fix spelling
---
 rpc/http.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 54 insertions(+), 3 deletions(-)

(limited to 'rpc/http.go')

diff --git a/rpc/http.go b/rpc/http.go
index 6717899b5..277f093a2 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -31,6 +31,7 @@ import (
 	"time"
 
 	"github.com/rs/cors"
+	"strings"
 )
 
 const (
@@ -148,8 +149,11 @@ func (t *httpReadWriteNopCloser) Close() error {
 // NewHTTPServer creates a new HTTP RPC server around an API provider.
 //
 // Deprecated: Server implements http.Handler
-func NewHTTPServer(cors []string, srv *Server) *http.Server {
-	return &http.Server{Handler: newCorsHandler(srv, cors)}
+func NewHTTPServer(cors []string, vhosts []string, srv *Server) *http.Server {
+	// Wrap the CORS-handler within a host-handler
+	handler := newCorsHandler(srv, cors)
+	handler = newVHostHandler(vhosts, handler)
+	return &http.Server{Handler: handler}
 }
 
 // ServeHTTP serves JSON-RPC requests over HTTP.
@@ -195,7 +199,6 @@ func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
 	if len(allowedOrigins) == 0 {
 		return srv
 	}
-
 	c := cors.New(cors.Options{
 		AllowedOrigins: allowedOrigins,
 		AllowedMethods: []string{http.MethodPost, http.MethodGet},
@@ -204,3 +207,51 @@ func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
 	})
 	return c.Handler(srv)
 }
+
+// virtualHostHandler is a handler which validates the Host-header of incoming requests.
+// The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers,
+// since they do in-domain requests against the RPC api. Instead, we can see on the Host-header
+// which domain was used, and validate that against a whitelist.
+type virtualHostHandler struct {
+	vhosts map[string]struct{}
+	next   http.Handler
+}
+
+// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
+func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// 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
+	if _, exist := h.vhosts["*"]; exist {
+		h.next.ServeHTTP(w, r)
+		return
+	}
+	if _, exist := h.vhosts[host]; exist {
+		h.next.ServeHTTP(w, r)
+		return
+	}
+	http.Error(w, "invalid host specified", http.StatusForbidden)
+	return
+}
+
+func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
+	vhostMap := make(map[string]struct{})
+	for _, allowedHost := range vhosts {
+		vhostMap[strings.ToLower(allowedHost)] = struct{}{}
+	}
+	return &virtualHostHandler{vhostMap, next}
+}
-- 
cgit v1.2.3


From 6c6247a690edbc9856b823fe9adfad6b43ce8e71 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende <martin@swende.se>
Date: Mon, 12 Feb 2018 14:12:55 +0100
Subject: node, rpc: fix linter issues

---
 rpc/http.go | 1 -
 1 file changed, 1 deletion(-)

(limited to 'rpc/http.go')

diff --git a/rpc/http.go b/rpc/http.go
index 277f093a2..a46d8c2b3 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -245,7 +245,6 @@ func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	http.Error(w, "invalid host specified", http.StatusForbidden)
-	return
 }
 
 func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
-- 
cgit v1.2.3