Last active
February 26, 2018 10:45
-
-
Save nl5887/2063706d815607805dd9 to your computer and use it in GitHub Desktop.
Gin-gonic middleware to prevent x-forwarded-for spoofing.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func ipInMasks(ip net.IP, masks []interface{}) bool { | |
for _, proxy := range masks { | |
var mask *net.IPNet | |
var err error | |
switch t := proxy.(type) { | |
case string: | |
if _, mask, err = net.ParseCIDR(t); err != nil { | |
panic(err) | |
} | |
case net.IP: | |
mask = &net.IPNet{IP: t, Mask: net.CIDRMask(len(t)*8, len(t)*8)} | |
case net.IPNet: | |
mask = &t | |
} | |
if mask.Contains(ip) { | |
return true | |
} | |
} | |
return false | |
} | |
// the ForwardedFor middleware unwraps the X-Forwarded-For headers, be careful to only use this | |
// middleware if you've got servers in front of this server. The list with (known) proxies and | |
// local ips are being filtered out of the forwarded for list, giving the last not local ip being | |
// the real client ip. | |
func ForwardedFor(proxies ...interface{}) HandlerFunc { | |
if len(proxies) == 0 { | |
// default to local ips | |
var reservedLocalIps = []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} | |
proxies = make([]interface{}, len(reservedLocalIps)) | |
for i, v := range reservedLocalIps { | |
proxies[i] = v | |
} | |
} | |
return func(c *Context) { | |
// the X-Forwarded-For header contains an array with left most the client ip, then | |
// comma separated, all proxies the request passed. The last proxy appears | |
// as the remote address of the request. Returning the client | |
// ip to comply with default RemoteAddr response. | |
// check if remoteaddr is local ip or in list of defined proxies | |
remoteIp := net.ParseIP(strings.Split(c.Request.RemoteAddr, ":")[0]) | |
if !ipInMasks(remoteIp, proxies) { | |
return | |
} | |
if forwardedFor := c.Request.Header.Get("X-Forwarded-For"); forwardedFor != "" { | |
parts := strings.Split(forwardedFor, ",") | |
for i := len(parts) - 1; i >= 0; i-- { | |
part := parts[i] | |
ip := net.ParseIP(strings.TrimSpace(part)) | |
if ipInMasks(ip, proxies) { | |
continue | |
} | |
// returning remote addr conform the original remote addr format | |
c.Request.RemoteAddr = ip.String() + ":0" | |
// remove forwarded for address | |
c.Request.Header.Set("X-Forwarded-For", "") | |
return | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func TestClientIPWithXForwardedForWithProxy(t *testing.T) { | |
r := New() | |
r.Use(ForwardedFor()) | |
var clientIP string = "" | |
r.GET("/", func(c *Context) { | |
clientIP = c.ClientIP() | |
}) | |
body := bytes.NewBuffer([]byte("")) | |
req, _ := http.NewRequest("GET", "/", body) | |
req.RemoteAddr = "172.16.8.3:1234" | |
req.Header.Set("X-Real-Ip", "realip") | |
req.Header.Set("X-Forwarded-For", "1.2.3.4, 10.10.0.4, 192.168.0.43, 172.16.8.4") | |
w := httptest.NewRecorder() | |
r.ServeHTTP(w, req) | |
if clientIP != "1.2.3.4:0" { | |
t.Errorf("ClientIP should not be %s, but 1.2.3.4:0", clientIP) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment