github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gorilla/handlers/proxy_headers.go (about) 1 package handlers 2 3 import ( 4 "github.com/hellobchain/newcryptosm/http" 5 "regexp" 6 "strings" 7 ) 8 9 var ( 10 // De-facto standard header keys. 11 xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") 12 xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") 13 xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") 14 xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme") 15 xRealIP = http.CanonicalHeaderKey("X-Real-IP") 16 ) 17 18 var ( 19 // RFC7239 defines a new "Forwarded: " header designed to replace the 20 // existing use of X-Forwarded-* headers. 21 // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43 22 forwarded = http.CanonicalHeaderKey("Forwarded") 23 // Allows for a sub-match of the first value after 'for=' to the next 24 // comma, semi-colon or space. The match is case-insensitive. 25 forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`) 26 // Allows for a sub-match for the first instance of scheme (http|https) 27 // prefixed by 'proto='. The match is case-insensitive. 28 protoRegex = regexp.MustCompile(`(?i)(?:proto=)(https|http)`) 29 ) 30 31 // ProxyHeaders inspects common reverse proxy headers and sets the corresponding 32 // fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP 33 // for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme 34 // for the scheme (http|https) and the RFC7239 Forwarded header, which may 35 // include both client IPs and schemes. 36 // 37 // NOTE: This middleware should only be used when behind a reverse 38 // proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are 39 // configured not to) strip these headers from client requests, or where these 40 // headers are accepted "as is" from a remote client (e.g. when Go is not behind 41 // a proxy), can manifest as a vulnerability if your application uses these 42 // headers for validating the 'trustworthiness' of a request. 43 func ProxyHeaders(h http.Handler) http.Handler { 44 fn := func(w http.ResponseWriter, r *http.Request) { 45 // Set the remote IP with the value passed from the proxy. 46 if fwd := getIP(r); fwd != "" { 47 r.RemoteAddr = fwd 48 } 49 50 // Set the scheme (proto) with the value passed from the proxy. 51 if scheme := getScheme(r); scheme != "" { 52 r.URL.Scheme = scheme 53 } 54 // Set the host with the value passed by the proxy 55 if r.Header.Get(xForwardedHost) != "" { 56 r.Host = r.Header.Get(xForwardedHost) 57 } 58 // Call the next handler in the chain. 59 h.ServeHTTP(w, r) 60 } 61 62 return http.HandlerFunc(fn) 63 } 64 65 // getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239 66 // Forwarded headers (in that order). 67 func getIP(r *http.Request) string { 68 var addr string 69 70 if fwd := r.Header.Get(xForwardedFor); fwd != "" { 71 // Only grab the first (client) address. Note that '192.168.0.1, 72 // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after 73 // the first may represent forwarding proxies earlier in the chain. 74 s := strings.Index(fwd, ", ") 75 if s == -1 { 76 s = len(fwd) 77 } 78 addr = fwd[:s] 79 } else if fwd := r.Header.Get(xRealIP); fwd != "" { 80 // X-Real-IP should only contain one IP address (the client making the 81 // request). 82 addr = fwd 83 } else if fwd := r.Header.Get(forwarded); fwd != "" { 84 // match should contain at least two elements if the protocol was 85 // specified in the Forwarded header. The first element will always be 86 // the 'for=' capture, which we ignore. In the case of multiple IP 87 // addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only 88 // extract the first, which should be the client IP. 89 if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { 90 // IPv6 addresses in Forwarded headers are quoted-strings. We strip 91 // these quotes. 92 addr = strings.Trim(match[1], `"`) 93 } 94 } 95 96 return addr 97 } 98 99 // getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 100 // Forwarded headers (in that order). 101 func getScheme(r *http.Request) string { 102 var scheme string 103 104 // Retrieve the scheme from X-Forwarded-Proto. 105 if proto := r.Header.Get(xForwardedProto); proto != "" { 106 scheme = strings.ToLower(proto) 107 } else if proto = r.Header.Get(xForwardedScheme); proto != "" { 108 scheme = strings.ToLower(proto) 109 } else if proto = r.Header.Get(forwarded); proto != "" { 110 // match should contain at least two elements if the protocol was 111 // specified in the Forwarded header. The first element will always be 112 // the 'proto=' capture, which we ignore. In the case of multiple proto 113 // parameters (invalid) we only extract the first. 114 if match := protoRegex.FindStringSubmatch(proto); len(match) > 1 { 115 scheme = strings.ToLower(match[1]) 116 } 117 } 118 119 return scheme 120 }