2014-12-01 4332 views
28

Cách chính xác để nhận tất cả Địa chỉ IP của khách hàng từ http.Request là gì? Trong PHP có rất nhiều variables mà tôi nên kiểm tra. Nó có giống nhau không?Cách chính xác để nhận Địa chỉ IP của Khách hàng từ http.Request (Golang)

Một mà tôi tìm thấy là:

req.RemoteAddr 

Và là trường hợp yêu cầu nhạy cảm? ví dụ: x-forwarded-for giống với X-Forwarded-ForX-FORWARDED-FOR? (từ req.Header.Get("X-FORWARDED-FOR"))

Trả lời

29

Nhìn vào http.Request bạn có thể tìm thấy các biến thành viên sau đây:

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

Bạn có thể sử dụng RemoteAddr để có được địa chỉ IP và cổng khách hàng từ xa (định dạng là "IP: port"), đó là địa chỉ của người yêu cầu ban đầu hoặc proxy cuối cùng (ví dụ: trình cân bằng tải ở trước máy chủ của bạn).

Đây là tất cả những gì bạn có.

Sau đó, bạn có thể điều tra các tiêu đề, đó là case-insensitive (mỗi tài liệu hướng dẫn ở trên), có nghĩa là tất cả các ví dụ của bạn sẽ làm việc và mang lại kết quả tương tự:

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

Điều này là do trong nội bộ http.Header.Get sẽ chuẩn hóa khóa cho bạn. (Nếu bạn muốn truy cập trực tiếp vào bản đồ tiêu đề và không thông qua Get, trước tiên bạn sẽ cần phải sử dụng http.CanonicalHeaderKey.)

Cuối cùng, "X-Forwarded-For" có lẽ là trường bạn muốn xem thêm thông tin về khách hàng IP. Điều này phụ thuộc rất nhiều vào phần mềm HTTP được sử dụng ở phía xa mặc dù, như khách hàng có thể đặt bất cứ điều gì trong đó nếu nó muốn. Ngoài ra, hãy lưu ý rằng expected format của trường này là danh sách địa chỉ IP được phân tách bằng dấu phẩy + dấu cách.Bạn sẽ cần phải phân tích nó một chút để có được một IP duy nhất của sự lựa chọn của bạn (có thể là người đầu tiên trong danh sách), ví dụ:

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

sẽ sản xuất:

10.0.0.1 
10.0.0.2 
10.0.0.3 
+0

Cách tôi đọc tài liệu cho 'req.Header', bạn chỉ cần thực hiện' req.Header.Get ("X-Forwarded-For") ', như các trường hợp khác được chuẩn hóa vào đó bởi trình phân tích cú pháp. –

+2

Có, tất nhiên, câu trả lời của tôi cho biết _all_ các biến thể sẽ hoạt động vì tiêu đề không phân biệt chữ hoa chữ thường. Tôi đã đề cập đến những điều này chỉ vì OP đã hỏi về độ nhạy của trường hợp, nhưng có thể thấy từ ngữ có thể gây nhầm lẫn. Sẽ nâng cấp. – tomasz

+0

yep, tiêu đề phân biệt chữ hoa chữ thường, nhưng bạn sẽ sử dụng 'http.CanonicalHeaderKey' thay vì thử tất cả các kết hợp chữ hoa và chữ thường. – bithavoc

1

Trong PHP có rất nhiều biến mà tôi nên kiểm tra. Nó có giống nhau không?

Điều này có không có gì để làm với Go (hoặc PHP cho vấn đề đó). Nó chỉ phụ thuộc vào những gì client, proxy, load-balancer, hoặc server đang gửi. Lấy cái bạn cần tùy thuộc vào môi trường của bạn.

http.Request.RemoteAddr chứa địa chỉ IP từ xa. Nó có thể hoặc không thể là khách hàng thực sự của bạn.

Và trường hợp yêu cầu có nhạy cảm không? ví dụ x-forwarded-for giống với X-Forwarded-For và X-FORWARDED-FOR? (từ req.Header.Get ("X-FORWARDED-FOR"))

Không, tại sao bạn không thử? http://play.golang.org/p/YMf_UBvDsH

+1

Headers luôn chuyển đổi sang định dạng MIME cùng trước khi thiết lập/nhận được, do đó lập luận req.Header.Get là case-insensitive. Nguồn https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey – k3a

4

Dưới đây là một hoàn toàn ví dụ làm việc

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)) 
} 
+0

Tại sao không chạy ParseIP trên "X-Forwarded-For"? –

+0

@ kristen: Câu hỏi hay. Quá lười biếng, tôi đoán;) –

+1

@ kristen: Trên thực tế, X-Forwarded-For là (có khả năng) một danh sách các địa chỉ IP (chuỗi proxy). Vì vậy, trước tiên bạn cần phải phân tách bằng ",". –

-4

Điều này phù hợp với tôi. Tôi chạy đi trong 8081 và thực hiện một yêu cầu từ cổng 8080.

fmt.Printf("r: %+v\n", r) // Print all fields that you get in request 

Output:

r: & {Phương pháp: URL POST:/email Proto: HTTP/1.1 ProtoMajor: 1 ProtoMinor: 1 Tiêu đề: bản đồ [Tác nhân người dùng: [Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.11 (KHTML, như Gecko) Phiên bản/10.0.1 Safari/602.2.11] Accept-Language: [en- us] Accept-Encoding: [gzip, deflate] Kết nối: [keep-alive] Chấp nhận: [/] Người giới thiệu: [http://127.0.0.1:8080/] Content-Length: [9] Content-Type: [application/x-www-form-urlencoded ; charset = UTF-8] Xuất xứ: [http://127.0.0.1:8080]] Nội dung: 0xc420012800 ContentLength: 9 TransferEncoding: [] Close: false Host: 127.0.0.1: 8081 Biểu mẫu: map [] PostForm: map [] MultipartForm: Đoạn giới thiệu: map [] RemoteAddr: 127.0 .0.1: 62232 RequestURI:/email TLS: Hủy: Trả lời: ctx: 0xc420017860}

Giới thiệu và xuất xứ có IP khách hàng của tôi.

ip := r.Referer() // Get Referer value 
fmt.Println(ip) // print ip 

Output:

http://127.0.0.1:8080/

Các vấn đề liên quan