Tôi biết có rất nhiều tiêu đề biến $ _SERVER có sẵn để truy xuất địa chỉ IP. Tôi đã tự hỏi nếu có một sự đồng thuận chung về cách lấy chính xác nhất địa chỉ IP thực của người dùng (biết rõ không có phương pháp nào là hoàn hảo) bằng cách sử dụng các biến đã nói?
Tôi đã dành một chút thời gian để cố gắng tìm một giải pháp chuyên sâu và đưa ra mã sau dựa trên một số nguồn. Tôi sẽ thích nó nếu ai đó có thể vui lòng chọc lỗ trong câu trả lời hoặc làm sáng tỏ một cái gì đó có lẽ chính xác hơn.
chỉnh sửa bao gồm tối ưu hóa từ @Alix
/**
* Retrieves the best guess of the client's actual IP address.
* Takes into account numerous HTTP proxy headers due to variations
* in how different ISPs handle IP addresses in headers between hops.
*/
public function get_ip_address() {
// Check for shared internet/ISP IP
if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
return $_SERVER['HTTP_CLIENT_IP'];
// Check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in var
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip) {
if ($this->validate_ip($ip))
return $ip;
}
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failed
return $_SERVER['REMOTE_ADDR'];
}
/**
* Ensures an IP address is both a valid IP address and does not fall within
* a private network range.
*
* @access public
* @param string $ip
*/
public function validate_ip($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 |
FILTER_FLAG_IPV6 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE) === false)
return false;
self::$ip = $ip;
return true;
}
Lời cảnh báo (cập nhật)
REMOTE_ADDR
vẫn đại diện cho nguồn đáng tin cậy nhất của một địa chỉ IP. Các $_SERVER
biến khác được đề cập ở đây có thể bị giả mạo bởi một máy khách từ xa rất dễ dàng. Mục đích của giải pháp này là cố gắng xác định địa chỉ IP của khách hàng ngồi phía sau proxy. Đối với mục đích chung của bạn, bạn có thể xem xét sử dụng kết hợp với địa chỉ IP được trả lại trực tiếp từ $_SERVER['REMOTE_ADDR']
và lưu trữ cả hai.
Đối với 99,9% người dùng, giải pháp này sẽ phù hợp với nhu cầu của bạn một cách hoàn hảo. Nó sẽ không bảo vệ bạn khỏi 0,1% người dùng độc hại đang tìm cách lạm dụng hệ thống của bạn bằng cách tiêm các tiêu đề yêu cầu của riêng họ. Nếu dựa vào địa chỉ IP cho một nhiệm vụ quan trọng nào đó, hãy dùng đến REMOTE_ADDR
và đừng bận tâm đến việc phục vụ những người đứng sau proxy.