Richtige Methode zum Abrufen der IP-Adressen des Clients von http.Request

76

Was ist der richtige Weg, um alle IP-Adressen des Clients abzurufen http.Request? Darin PHPgibt es viele Variablen , die ich überprüfen sollte. Ist es das gleiche auf Go?

Eine, die ich gefunden habe, ist:

req.RemoteAddr

Und unterscheidet die Anfrage zwischen Groß- und Kleinschreibung? zum Beispiel x-forwarded-forist das gleiche wie X-Forwarded-Forund X-FORWARDED-FOR? (von req.Header.Get("X-FORWARDED-FOR"))

Kokizzu
quelle

Antworten:

84

Unter http.Request finden Sie die folgenden Mitgliedsvariablen:

// HTTP defines that header names are case-insensitive.
// The request parser implements this by canonicalizing the
// name, making the first character and any characters
// following a hyphen uppercase and the rest lowercase.
//
// For client requests certain headers are automatically
// added and may override values in Header.
//
// See the documentation for the Request.Write method.
Header Header

// RemoteAddr allows HTTP servers and other software to record
// the network address that sent the request, usually for
// logging. This field is not filled in by ReadRequest and
// has no defined format. The HTTP server in this package
// sets RemoteAddr to an "IP:port" address before invoking a
// handler.
// This field is ignored by the HTTP client.
RemoteAddr string

Sie können RemoteAddrdie IP-Adresse und den Port des Remoteclients abrufen (das Format lautet "IP: Port"). Dies ist die Adresse des ursprünglichen Anforderers oder des letzten Proxys (z. B. eines Load Balancers, der sich vor Ihrem Server befindet).

Das ist alles was Sie sicher haben.

Anschließend können Sie die Überschriften untersuchen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird (siehe obige Dokumentation). Dies bedeutet, dass alle Ihre Beispiele funktionieren und dasselbe Ergebnis liefern:

req.Header.Get("X-Forwarded-For") // capitalisation
req.Header.Get("x-forwarded-for") // doesn't
req.Header.Get("X-FORWARDED-FOR") // matter

Dies liegt daran, http.Header.Getdass der Schlüssel intern für Sie normalisiert wird. (Wenn Sie direkt und nicht über die Header-Map zugreifen Getmöchten, müssen Sie zuerst http.CanonicalHeaderKey verwenden .)

Schließlich "X-Forwarded-For"ist wahrscheinlich das Feld, das Sie sich ansehen möchten, um weitere Informationen über die IP des Clients zu erhalten. Dies hängt jedoch stark von der auf der Remote-Seite verwendeten HTTP-Software ab, da der Client auf Wunsch alles dort ablegen kann. Beachten Sie außerdem, dass das erwartete Format dieses Felds die durch Kommas und Leerzeichen getrennte Liste der IP-Adressen ist. Sie müssen es ein wenig analysieren, um eine einzelne IP Ihrer Wahl zu erhalten (wahrscheinlich die erste in der Liste), zum Beispiel:

// Assuming format is as expected
ips := strings.Split("10.0.0.1, 10.0.0.2, 10.0.0.3", ", ")
for _, ip := range ips {
    fmt.Println(ip)
}

wird herstellen:

10.0.0.1
10.0.0.2
10.0.0.3
tomasz
quelle
1
Die Art und Weise, wie ich die Dokumentation gelesen habe req.Header, müssen Sie nur tun req.Header.Get("X-Forwarded-For"), da die anderen Fälle vom Parser kanonisiert werden.
Lars Haugseth
2
Ja, meine Antwort besagt natürlich, dass alle Variationen funktionieren, da bei Überschriften die Groß- und Kleinschreibung nicht berücksichtigt wird. Ich habe diese nur erwähnt, weil OP nach der Groß- und Kleinschreibung gefragt hat, aber der Wortlaut könnte verwirrend sein. Werde dich auf den neuesten Stand bringen.
Thomasz
Ja, die Überschriften unterscheiden zwischen http.CanonicalHeaderKeyGroß- und Kleinschreibung , aber Sie würden sie verwenden, anstatt alle möglichen Kombinationen von Groß- und Kleinbuchstaben auszuprobieren .
Bithavoc
Sie sollten sich vor "X-Forwarded-For" -Spoofing durch den Client hüten. Das erste äußere Gateway Ihres Dienstes sollte wahrscheinlich einen solchen vom Client bereitgestellten Header entfernen.
Falco
21

Hier ein voll funktionsfähiges Beispiel

package main

import (  
    // Standard library packages
    "fmt"
    "strconv"
    "log"
    "net"
    "net/http"

    // Third party packages
    "github.com/julienschmidt/httprouter"
    "github.com/skratchdot/open-golang/open"
)



// https://blog.golang.org/context/userip/userip.go
func getIP(w http.ResponseWriter, req *http.Request, _ httprouter.Params){
    fmt.Fprintf(w, "<h1>static file server</h1><p><a href='./static'>folder</p></a>")

    ip, port, err := net.SplitHostPort(req.RemoteAddr)
    if err != nil {
        //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)

        fmt.Fprintf(w, "userip: %q is not IP:port", req.RemoteAddr)
    }

    userIP := net.ParseIP(ip)
    if userIP == nil {
        //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
        fmt.Fprintf(w, "userip: %q is not IP:port", req.RemoteAddr)
        return
    }

    // This will only be defined when site is accessed via non-anonymous proxy
    // and takes precedence over RemoteAddr
    // Header.Get is case-insensitive
    forward := req.Header.Get("X-Forwarded-For")

    fmt.Fprintf(w, "<p>IP: %s</p>", ip)
    fmt.Fprintf(w, "<p>Port: %s</p>", port)
    fmt.Fprintf(w, "<p>Forwarded for: %s</p>", forward)
}


func main() {  
    myport := strconv.Itoa(10002);


    // Instantiate a new router
    r := httprouter.New()

    r.GET("/ip", getIP)

    // Add a handler on /test
    r.GET("/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        // Simply write some test data for now
        fmt.Fprint(w, "Welcome!\n")
    })  


    l, err := net.Listen("tcp", "localhost:" + myport)
    if err != nil {
        log.Fatal(err)
    }
    // The browser can connect now because the listening socket is open.


    //err = open.Start("http://localhost:"+ myport + "/test")
    err = open.Start("http://localhost:"+ myport + "/ip")
    if err != nil {
         log.Println(err)
    }

    // Start the blocking server loop.
    log.Fatal(http.Serve(l, r)) 
}
Stefan Steiger
quelle
Warum nicht ParseIP auf "X-Forwarded-For" ausführen?
2
@kristen: Tatsächlich ist X-Forwarded-For (möglicherweise) eine Liste von IP-Adressen (Proxy-Verkettung). Sie müssen also zuerst durch "," teilen.
Stefan Steiger
10

So komme ich auf die IP

func ReadUserIP(r *http.Request) string {
    IPAddress := r.Header.Get("X-Real-Ip")
    if IPAddress == "" {
        IPAddress = r.Header.Get("X-Forwarded-For")
    }
    if IPAddress == "" {
        IPAddress = r.RemoteAddr
    }
    return IPAddress
}
  • X-Real-Ip - ruft die erste echte IP ab (wenn sich die Anforderungen hinter mehreren NAT-Quellen / Load Balancer befinden)

  • X-Forwarded-For - Wenn X-Real-Ip aus irgendeinem Grund leer ist und keine Antwort zurückgibt, wenden Sie sich an X-Forwarded-For

  • Remote-Adresse - letzter Ausweg (normalerweise nicht zuverlässig, da dies möglicherweise die letzte IP ist oder wenn es sich um eine nackte http-Anfrage an den Server handelt, dh kein Load Balancer)
mel3kings
quelle
4
Warnung: Überprüfen Sie die Kommentare zu dieser Antwort - stackoverflow.com/a/55790/1584308 - "Der Client kann den X-Forwarded-Foroder den X-Real-IPHeader auf einen beliebigen Wert setzen. Wenn Sie keinen vertrauenswürdigen Reverse-Proxy haben, sollten Sie keinen verwenden diese Werte. "
Hewiefreeman
3

In PHP gibt es viele Variablen, die ich überprüfen sollte. Ist es das gleiche auf Go?

Dies hat nichts mit Go (oder PHP) zu tun. Es hängt nur davon ab, was der Client, Proxy, Load Balancer oder Server sendet. Holen Sie sich das, was Sie je nach Umgebung benötigen.

http.Request.RemoteAddrenthält die Remote-IP-Adresse. Es kann Ihr tatsächlicher Kunde sein oder nicht.

Und unterscheidet die Anfrage zwischen Groß- und Kleinschreibung? Zum Beispiel ist x-forwarded-for dasselbe wie X-Forwarded-For und X-FORWARDED-FOR? (von req.Header.Get ("X-FORWARDED-FOR"))

Nein, warum probieren Sie es nicht selbst aus? http://play.golang.org/p/YMf_UBvDsH

JimB
quelle
1
Header werden vor dem Festlegen / Abrufen immer in dasselbe MIME-Format konvertiert. Daher wird bei dem Argument req.Header.Get die Groß- und Kleinschreibung nicht berücksichtigt. Quelle golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey
k3a
1

Laut Mozilla MDN: " Der X-Forwarded-For (XFF) -Header ist ein De-facto-Standardheader zum Identifizieren der ursprünglichen IP-Adresse eines Clients. "
Sie veröffentlichen klare Informationen in ihrem X-Forwarded-For- Artikel.

Juan Lanus
quelle