Skip to content

Instantly share code, notes, and snippets.

@17twenty
Created January 18, 2017 04:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 17twenty/c815680c9c585cd9c16e62cbee7317b6 to your computer and use it in GitHub Desktop.
Save 17twenty/c815680c9c585cd9c16e62cbee7317b6 to your computer and use it in GitHub Desktop.
Extracting X-Forwarded-For from connections in Golang
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func main() {
th := loggingMiddleware(http.HandlerFunc(handler))
mux := http.NewServeMux()
mux.Handle("/", th)
log.Fatal(http.ListenAndServe(":9000", mux))
}
func loggingMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
ipAddress := req.RemoteAddr
fwdAddress := req.Header.Get("X-Forwarded-For") // capitalisation doesn't matter
if fwdAddress != "" {
// Got X-Forwarded-For
ipAddress = fwdAddress // If it's a single IP, then awesome!
// If we got an array... grab the first IP
ips := strings.Split(fwdAddress, ", ")
if len(ips) > 1 {
ipAddress = ips[0]
}
}
log.Println("Got connection from ", ipAddress)
h.ServeHTTP(rw, req)
})
}
func handler(w http.ResponseWriter, r *http.Request) {
log.Println("Here!")
fmt.Fprintf(w, "Sup dawgs")
}
@JSmith-Aura
Copy link

JSmith-Aura commented Dec 15, 2023

This is not a correct way of parsing X-Forwarded-For, the left most value is untrusted and can be spoofed in many situations.

When choosing the first trustworthy X-Forwarded-For client IP address, additional configuration is required. There are two common methods:

Trusted proxy count:
The count of reverse proxies between the internet and the server is configured. The X-Forwarded-For IP list is searched from the rightmost by that count minus one. (For example, if there is only one reverse proxy, that proxy will add the client's IP address, so the rightmost address should be used. If there are three reverse proxies, the last two IP addresses will be internal.)

Trusted proxy list: The IPs or IP ranges of the trusted reverse proxies are configured. The X-Forwarded-For IP list is searched from the rightmost, skipping all addresses that are on the trusted proxy list. The first non-matching address is the target address.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For

Here is a little snippet that will do it correctly, although you may need to add a configuration of how many trusted proxies are infront of your application

func GetIPFromRequest(r *http.Request) string {

	//Do not respect the X-Forwarded-For header until we are explictly told we are being proxied.
	if config.Values.NumberProxies > 0 {
		ips := r.Header.Get("X-Forwarded-For")
		addresses := strings.Split(ips, ",")

		if ips != "" && len(addresses) > 0 {

			if len(addresses)-config.Values.NumberProxies < 0 {
				log.Println("WARNING XFF parsing may be broken: ", len(addresses)-config.Values.NumberProxies, " check config.Values.NumberProxies")
				return net.ParseIP(strings.TrimSpace(addresses[len(addresses)-1])).String()
			}

			return net.ParseIP(strings.TrimSpace(addresses[len(addresses)-config.Values.NumberProxies])).String()
		}
	}

	return net.ParseIP(GetIP(r.RemoteAddr)).String()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment