mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 14:52:18 +08:00
refactor(): fix client Ip vulnerability
This commit is contained in:
parent
31a2afa57f
commit
c9ea8ece4a
110
context.go
110
context.go
@ -729,13 +729,13 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e
|
|||||||
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
||||||
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
|
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
|
||||||
func (c *Context) ClientIP() string {
|
func (c *Context) ClientIP() string {
|
||||||
if c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
|
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
|
||||||
for _, header := range c.engine.RemoteIPHeaders {
|
if err != nil {
|
||||||
ipChain := filterIPsFromUntrustedProxies(c.requestHeader(header), c.Request, c.engine)
|
return ""
|
||||||
if len(ipChain) > 0 {
|
|
||||||
return ipChain[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
remoteIP := net.ParseIP(ip)
|
||||||
|
if remoteIP == nil {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.engine.AppEngine {
|
if c.engine.AppEngine {
|
||||||
@ -744,82 +744,46 @@ func (c *Context) ClientIP() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, _ := getTransportPeerIPForRequest(c.Request)
|
if c.shouldCheckIPHeaders() {
|
||||||
|
for _, cidr := range c.engine.trustedCIDRs {
|
||||||
|
if cidr.Contains(remoteIP) {
|
||||||
|
for _, headerName := range c.engine.RemoteIPHeaders {
|
||||||
|
ip, valid := validateHeader(c.requestHeader(headerName))
|
||||||
|
if valid {
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterIPsFromUntrustedProxies(XForwardedForHeader string, req *http.Request, e *Engine) []string {
|
|
||||||
var items, out []string
|
|
||||||
if XForwardedForHeader != "" {
|
|
||||||
items = strings.Split(XForwardedForHeader, ",")
|
|
||||||
} else {
|
|
||||||
return []string{}
|
|
||||||
}
|
}
|
||||||
if peerIP, err := getTransportPeerIPForRequest(req); err == nil {
|
}
|
||||||
items = append(items, peerIP)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(items) - 1; i >= 0; i-- {
|
return remoteIP.String()
|
||||||
item := strings.TrimSpace(items[i])
|
}
|
||||||
ip := net.ParseIP(item)
|
|
||||||
|
func (c *Context) shouldCheckIPHeaders() bool {
|
||||||
|
return c.engine.ForwardedByClientIP &&
|
||||||
|
c.engine.RemoteIPHeaders != nil &&
|
||||||
|
len(c.engine.RemoteIPHeaders) > 0 &&
|
||||||
|
c.engine.trustedCIDRs != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHeader(header string) (clientIP string, valid bool) {
|
||||||
|
if header == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items := strings.Split(header, ",")
|
||||||
|
for i, ipStr := range items {
|
||||||
|
ipStr = strings.TrimSpace(ipStr)
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return out
|
return "", false
|
||||||
}
|
}
|
||||||
|
if i == 0 {
|
||||||
out = prependString(ip.String(), out)
|
clientIP = ipStr
|
||||||
if !isTrustedProxy(ip, e) {
|
valid = true
|
||||||
return out
|
|
||||||
}
|
|
||||||
// out = prependString(ip.String(), out)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func isTrustedProxy(ip net.IP, e *Engine) bool {
|
|
||||||
for _, trustedProxy := range e.TrustedProxies {
|
|
||||||
if _, ipnet, err := net.ParseCIDR(trustedProxy); err == nil {
|
|
||||||
if ipnet.Contains(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if proxyIP := net.ParseIP(trustedProxy); proxyIP != nil {
|
|
||||||
if proxyIP.Equal(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if addrs, err := e.lookupHost(trustedProxy); err == nil {
|
|
||||||
for _, proxyAddr := range addrs {
|
|
||||||
proxyIP := net.ParseIP(proxyAddr)
|
|
||||||
if proxyIP == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if proxyIP.Equal(ip) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func prependString(ip string, ipList []string) []string {
|
|
||||||
ipList = append(ipList, "")
|
|
||||||
copy(ipList[1:], ipList)
|
|
||||||
ipList[0] = string(ip)
|
|
||||||
return ipList
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTransportPeerIPForRequest(req *http.Request) (string, error) {
|
|
||||||
var err error
|
|
||||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(req.RemoteAddr)); err == nil {
|
|
||||||
return ip, nil
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContentType returns the Content-Type header of the request.
|
// ContentType returns the Content-Type header of the request.
|
||||||
|
25
gin.go
25
gin.go
@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||||
@ -118,6 +119,7 @@ type Engine struct {
|
|||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
trees methodTrees
|
trees methodTrees
|
||||||
maxParams uint16
|
maxParams uint16
|
||||||
|
trustedCIDRs []*net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IRouter = &Engine{}
|
var _ IRouter = &Engine{}
|
||||||
@ -312,12 +314,35 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
|||||||
func (engine *Engine) Run(addr ...string) (err error) {
|
func (engine *Engine) Run(addr ...string) (err error) {
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
|
trustedCIDRs, err := engine.prepareCIDR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
engine.trustedCIDRs = trustedCIDRs
|
||||||
address := resolveAddress(addr)
|
address := resolveAddress(addr)
|
||||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||||
err = http.ListenAndServe(address, engine)
|
err = http.ListenAndServe(address, engine)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (engine *Engine) prepareCIDR() ([]*net.IPNet, error) {
|
||||||
|
if engine.TrustedProxies != nil {
|
||||||
|
cidr := make([]*net.IPNet, len(engine.TrustedProxies), 0)
|
||||||
|
for _, trustedProxy := range engine.TrustedProxies {
|
||||||
|
if strings.Contains(trustedProxy, "/") {
|
||||||
|
trustedProxy += "/32"
|
||||||
|
}
|
||||||
|
_, cidrNet, err := net.ParseCIDR(trustedProxy)
|
||||||
|
if err != nil {
|
||||||
|
return cidr, err
|
||||||
|
}
|
||||||
|
cidr = append(cidr, cidrNet)
|
||||||
|
}
|
||||||
|
return cidr, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
||||||
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user