web-dev-qa-db-ja.com

http.RequestからクライアントのIPアドレスを取得する正しい方法

http.RequestからすべてのクライアントのIPアドレスを取得する正しい方法は何ですか? PHPには、チェックする必要のある 変数 がたくさんあります。 Goでも同じですか?

私が見つけたものは:

req.RemoteAddr

リクエストの大文字と小文字は区別されますか?たとえば、x-forwarded-forX-Forwarded-ForおよびX-FORWARDED-FOR?と同じです。 (req.Header.Get("X-FORWARDED-FOR")から)

55
Kokizzu

http.Request を見ると、次のメンバー変数を見つけることができます。

// 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

RemoteAddrを使用して、リモートクライアントのIPアドレスとポート(形式は「IP:port」)を取得できます。これは元のリクエスターまたは最後のプロキシ(たとえば、サーバーの前にあるロードバランサー)。

これはあなたが確かに持っているすべてです。

次に、ヘッダーを調査できます。ヘッダーはcase-insensitive(上記のドキュメントごと)です。つまり、すべての例が機能し、同じ結果が得られます。

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

これは、内部的にhttp.Header.Getがキーを正規化するためです。 (Get経由ではなく、ヘッダーマップに直接アクセスする場合は、最初に http.CanonicalHeaderKey を使用する必要があります。)

最後に、"X-Forwarded-For"は、おそらくクライアントのIPに関する詳細情報を取得するために見たいフィールドです。ただし、クライアントは必要に応じてそこに何かを入れることができるため、これはリモート側で使用されるHTTPソフトウェアに大きく依存します。また、このフィールドの expected形式 はIPアドレスのコンマ+スペース区切りリストであることに注意してください。選択した単一のIP(おそらくリストの最初のIP)を取得するには、少し解析する必要があります。次に例を示します。

// 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)
}

生成されます:

10.0.0.1
10.0.0.2
10.0.0.3
66
tomasz

ここに完全に動作する例

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)) 
}
13
Stefan Steiger

これは私が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-最初の真のIPを取得します(リクエストが複数のNATソース/ロードバランサーの背後にある場合)

  • X-Forwarded-For-何らかの理由でX-Real-Ipが空白で応答を返さない場合、X-Forwarded-Forから取得

  • リモートアドレス-最後の手段(通常、これは最後のIPである可能性があるため、またはサーバーへのネイキッドHTTPリクエストである場合、つまりロードバランサーがない場合、信頼性が低くなります)
4
mel3kings

PHPには、確認する必要のある変数がたくさんあります。 Goでも同じですか?

これには、Go(またはその場合はPHP)とは関係ないnothingがあります。それは、クライアント、プロキシ、ロードバランサー、またはサーバーが送信しているものによって異なります。環境に応じて必要なものを入手してください。

http.Request.RemoteAddrにはリモートIPアドレスが含まれます。実際のクライアントである場合とそうでない場合があります。

リクエストの大文字と小文字は区別されますか?たとえば、x-forwarded-forはX-Forwarded-ForおよびX-FORWARDED-FORと同じですか? (req.Header.Get( "X-FORWARDED-FOR")から))

いいえ、自分で試してみませんか? http://play.golang.org/p/YMf_UBvDsH

2
JimB