Refactor and fix

This commit is contained in:
Notealot 2021-11-29 11:14:02 +08:00
parent 7d07e39b2e
commit 0b56a60012
4 changed files with 59 additions and 48 deletions

View File

@ -78,7 +78,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [http2 server push](#http2-server-push) - [http2 server push](#http2-server-push)
- [Define format for the log of routes](#define-format-for-the-log-of-routes) - [Define format for the log of routes](#define-format-for-the-log-of-routes)
- [Set and get a cookie](#set-and-get-a-cookie) - [Set and get a cookie](#set-and-get-a-cookie)
- [Don't trust all proxies](#don't-trust-all-proxies) - [Don't trust all proxies](#do-not-trust-all-proxies)
- [Testing](#testing) - [Testing](#testing)
- [Users](#users) - [Users](#users)
@ -2197,7 +2197,7 @@ func main() {
} }
``` ```
## Don't trust all proxies ## Do not trust all proxies
Gin lets you specify which headers to hold the real client IP (if any), Gin lets you specify which headers to hold the real client IP (if any),
as well as specifying which proxies (or direct clients) you trust to as well as specifying which proxies (or direct clients) you trust to

View File

@ -757,10 +757,14 @@ func (c *Context) ClientIP() string {
} }
} }
remoteIP, trusted := c.RemoteIP() // It also checks if the remoteIP is a trusted proxy or not.
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
// defined by Engine.SetTrustedProxies()
remoteIP := net.ParseIP(c.RemoteIP())
if remoteIP == nil { if remoteIP == nil {
return "" return ""
} }
trusted := c.engine.isTrustedProxy(remoteIP)
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
for _, headerName := range c.engine.RemoteIPHeaders { for _, headerName := range c.engine.RemoteIPHeaders {
@ -773,53 +777,13 @@ func (c *Context) ClientIP() string {
return remoteIP.String() return remoteIP.String()
} }
func (e *Engine) isTrustedProxy(ip net.IP) bool {
if e.trustedCIDRs != nil {
for _, cidr := range e.trustedCIDRs {
if cidr.Contains(ip) {
return true
}
}
}
return false
}
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port). // RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
// It also checks if the remoteIP is a trusted proxy or not. func (c *Context) RemoteIP() string {
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
// defined by Engine.SetTrustedProxies()
func (c *Context) RemoteIP() (net.IP, bool) {
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
if err != nil { if err != nil {
return nil, false return ""
} }
remoteIP := net.ParseIP(ip) return ip
if remoteIP == nil {
return nil, false
}
return remoteIP, c.engine.isTrustedProxy(remoteIP)
}
func (e *Engine) validateHeader(header string) (clientIP string, valid bool) {
if header == "" {
return "", false
}
items := strings.Split(header, ",")
for i := len(items) - 1; i >= 0; i-- {
ipStr := strings.TrimSpace(items[i])
ip := net.ParseIP(ipStr)
if ip == nil {
return "", false
}
// X-Forwarded-For is appended by proxy
// Check IPs in reverse order and stop when find untrusted proxy
if (i == 0) || (!e.isTrustedProxy(ip)) {
return ipStr, true
}
}
return
} }
// ContentType returns the Content-Type header of the request. // ContentType returns the Content-Type header of the request.

View File

@ -12,6 +12,7 @@ import (
"html/template" "html/template"
"io" "io"
"mime/multipart" "mime/multipart"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@ -1404,6 +1405,11 @@ func TestContextClientIP(t *testing.T) {
// Tests exercising the TrustedProxies functionality // Tests exercising the TrustedProxies functionality
resetContextForClientIPTests(c) resetContextForClientIPTests(c)
// IPv6 support
c.Request.RemoteAddr = "[::1]:12345"
assert.Equal(t, "20.20.20.20", c.ClientIP())
resetContextForClientIPTests(c)
// No trusted proxies // No trusted proxies
_ = c.engine.SetTrustedProxies([]string{}) _ = c.engine.SetTrustedProxies([]string{})
c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"} c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
@ -1500,6 +1506,7 @@ func resetContextForClientIPTests(c *Context) {
c.Request.Header.Set("CF-Connecting-IP", "60.60.60.60") c.Request.Header.Set("CF-Connecting-IP", "60.60.60.60")
c.Request.RemoteAddr = " 40.40.40.40:42123 " c.Request.RemoteAddr = " 40.40.40.40:42123 "
c.engine.TrustedPlatform = "" c.engine.TrustedPlatform = ""
c.engine.trustedCIDRs = defaultTrustedCIDRs
c.engine.AppEngine = false c.engine.AppEngine = false
} }
@ -2051,7 +2058,8 @@ func TestRemoteIPFail(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.RemoteAddr = "[:::]:80" c.Request.RemoteAddr = "[:::]:80"
ip, trust := c.RemoteIP() ip := net.ParseIP(c.RemoteIP())
trust := c.engine.isTrustedProxy(ip)
assert.Nil(t, ip) assert.Nil(t, ip)
assert.False(t, trust) assert.False(t, trust)
} }

41
gin.go
View File

@ -28,7 +28,13 @@ var (
var defaultPlatform string var defaultPlatform string
var defaultTrustedCIDRs = []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}} // 0.0.0.0/0 var defaultTrustedCIDRs = []*net.IPNet{
{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}, // 0.0.0.0/0
{ // ::/0
IP: net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
Mask: net.IPMask{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
},
}
// HandlerFunc defines the handler used by gin middleware as return value. // HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context) type HandlerFunc func(*Context)
@ -411,6 +417,39 @@ func (engine *Engine) parseTrustedProxies() error {
return err return err
} }
// isTrustedProxy will check whether the IP address is included in the trusted list according to Engine.trustedCIDRs
func (engine *Engine) isTrustedProxy(ip net.IP) bool {
if engine.trustedCIDRs != nil {
for _, cidr := range engine.trustedCIDRs {
if cidr.Contains(ip) {
return true
}
}
}
return false
}
// validateHeader will parse X-Forwarded-For header and return the trusted client IP address
func (engine *Engine) validateHeader(header string) (clientIP string, valid bool) {
if header != "" {
items := strings.Split(header, ",")
for i := len(items) - 1; i >= 0; i-- {
ipStr := strings.TrimSpace(items[i])
ip := net.ParseIP(ipStr)
if ip == nil {
break
}
// X-Forwarded-For is appended by proxy
// Check IPs in reverse order and stop when find untrusted proxy
if (i == 0) || (!engine.isTrustedProxy(ip)) {
return ipStr, true
}
}
}
return "", false
}
// parseIP parse a string representation of an IP and returns a net.IP with the // parseIP parse a string representation of an IP and returns a net.IP with the
// minimum byte representation or nil if input is invalid. // minimum byte representation or nil if input is invalid.
func parseIP(ip string) net.IP { func parseIP(ip string) net.IP {