Cách chính xác nhất để lấy địa chỉ IP chính xác của người dùng trong PHP là gì?


301

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_ADDRvẫn đại diện cho nguồn đáng tin cậy nhất của một địa chỉ IP. Các $_SERVERbiế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_ADDRvà đừng bận tâm đến việc phục vụ những người đứng sau proxy.


2
Đối với câu hỏi whatismyip.com, tôi nghĩ họ làm một cái gì đó giống như kịch bản này, bạn có đang chạy nó cục bộ không? Nếu bạn là lý do tại sao bạn có IP nội bộ, không có gì được gửi qua giao diện công cộng trong trường hợp đó vì vậy không có thông tin nào để php nhận được
Matt

2
Hãy chắc chắn rằng bạn ghi nhớ điều này khi thực hiện điều này: stackoverflow.com/questions/1672827/NH
Kevin Peno

19
Hãy nhớ rằng tất cả các tiêu đề HTTP này rất dễ sửa đổi: với giải pháp của bạn, tôi chỉ cần định cấu hình trình duyệt của mình để gửi tiêu đề X-Forwarded-For với một IP ngẫu nhiên và tập lệnh của bạn sẽ vui vẻ trả lại địa chỉ giả. Vì vậy, tùy thuộc vào những gì bạn đang cố gắng thực hiện, giải pháp này có thể kém tin cậy hơn so với việc sử dụng REMOTE_ADDR.
gnomnain

14
OMFG, "ip không đáng tin cậy"! Lần đầu tiên tôi thấy một điều vô nghĩa như vậy ở đây trên SO. Địa chỉ IP đáng tin cậy duy nhất là REMOTE_ADDR
Ý thức chung của bạn

3
-1 điều này dễ bị giả mạo. Tất cả những gì bạn đang làm là hỏi người dùng địa chỉ IP của anh ta nên là gì.
rook

Câu trả lời:


269

Đây là một cách ngắn hơn, sạch hơn để lấy địa chỉ IP:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

Tôi hy vọng nó sẽ giúp!


Mã của bạn dường như đã khá hoàn chỉnh, tôi không thể thấy bất kỳ lỗi nào có thể xảy ra trong đó (ngoài các thông báo IP thông thường), tôi sẽ thay đổi validate_ip()chức năng để dựa vào tiện ích mở rộng bộ lọc:

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

Ngoài ra HTTP_X_FORWARDED_FORđoạn trích của bạn có thể được đơn giản hóa từ đây:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

Về điều này:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

Bạn cũng có thể muốn xác thực địa chỉ IPv6.


4
Tôi chắc chắn đánh giá cao sự filter_varkhắc phục vì nó loại bỏ một loạt các kiểm tra int unsish hack trên địa chỉ IP. Tôi cũng thích thực tế là nó cũng cho tôi tùy chọn xác thực các địa chỉ IPv6. Việc HTTP_X_FORWARDED_FORtối ưu hóa cũng được nhiều đánh giá cao. Trong vài phút nữa tôi sẽ cập nhật mã.
Corey Ballou

33
-1 điều này dễ bị giả mạo tất cả những gì bạn đang làm là hỏi người dùng địa chỉ IP của anh ta nên là gì.
rook

7
@Rook: Vâng, tôi biết. OP nhận thức được điều đó và tôi cũng đã đề cập đến nó trong câu trả lời của mình. Nhưng cảm ơn vì nhận xét.
Alix Axel

1
FYI: Tôi đã phải xóa FILTER_FLAG_IPV6 để mã Alix Axel hoạt động.
darkAsPitch

2
@ rubenrp81 Trình xử lý ổ cắm TCP là nguồn chính tắc duy nhất, mọi thứ khác đều do kẻ tấn công kiểm soát. Đoạn mã trên là giấc mơ của kẻ tấn công.
rook

12

Tuy nhiên, ngay cả khi đó, việc có được địa chỉ IP thực của người dùng sẽ không đáng tin cậy. Tất cả những gì họ cần làm là sử dụng máy chủ proxy ẩn danh (một máy chủ không tôn trọng các tiêu đề cho http_x_forwarded_for, http_forwarded, v.v.) và tất cả những gì bạn nhận được là địa chỉ IP của máy chủ proxy của họ.

Sau đó, bạn có thể xem liệu có một danh sách các địa chỉ IP máy chủ proxy ẩn danh hay không, nhưng không có cách nào để chắc chắn rằng nó chính xác 100% và điều quan trọng nhất là hãy cho bạn biết đó là máy chủ proxy. Và nếu ai đó thông minh, họ có thể giả mạo các tiêu đề cho HTTP chuyển tiếp.

Hãy nói rằng tôi không thích trường đại học địa phương. Tôi tìm ra địa chỉ IP nào họ đã đăng ký và khiến địa chỉ IP của họ bị cấm trên trang web của bạn bằng cách thực hiện những điều xấu, bởi vì tôi nhận ra rằng bạn tôn trọng HTTP chuyển tiếp. Danh sách là vô tận.

Sau đó, như bạn đoán, các địa chỉ IP nội bộ như mạng đại học mà tôi đã gặp trước đây. Rất nhiều sử dụng định dạng 10.xxx. Vì vậy, tất cả những gì bạn sẽ biết là nó đã được chuyển tiếp cho một mạng chia sẻ.

Sau đó, tôi sẽ không bắt đầu nhiều vào nó, nhưng địa chỉ IP động là cách băng thông rộng nữa. Vì thế. Ngay cả khi bạn nhận được địa chỉ IP người dùng, hãy mong đợi nó sẽ thay đổi sau 2 - 3 tháng, lâu nhất.


Cảm ơn các đầu vào. Tôi hiện đang sử dụng địa chỉ IP của người dùng để hỗ trợ xác thực phiên bằng cách sử dụng IP lớp C của họ làm yếu tố giới hạn để hạn chế chiếm quyền điều khiển phiên nhưng cho phép IP động trong lý do. IP giả mạo và máy chủ proxy ẩn danh chỉ là thứ tôi sẽ phải đối phó với một nhóm cá nhân được chọn.
Corey Ballou

@cballou - Chắc chắn cho mục đích này REMOTE_ADDR là cách sử dụng chính xác. Bất kỳ cách tiếp cận nào dựa vào tiêu đề HTTP đều dễ bị giả mạo tiêu đề. Bao lâu là một phiên? IP động không thay đổi nhanh.
MZB

Họ làm, đặc biệt là nếu tôi muốn họ (thay đổi địa chỉ mac mà nhiều trình điều khiển hỗ trợ). Chỉ cần REMOTE_ADDR là đủ để có được máy chủ cuối cùng mà nó nói đến. Vì vậy, trong một tình huống proxy, bạn nhận được IP proxy.

8

Chúng tôi sử dụng:

/**
 * Get the customer's IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

Vụ nổ trên HTTP_X_FORWARDED_FOR là do các vấn đề kỳ lạ mà chúng tôi đã phát hiện địa chỉ IP khi Squid được sử dụng.


Rất tiếc, tôi mới nhận ra bạn về cơ bản là làm điều tương tự với việc nổ tung, v.v. Cộng thêm một chút. Vì vậy, tôi nghi ngờ câu trả lời của tôi đã được giúp đỡ nhiều. :)
gabrielk

Điều này trả về địa chỉ của localhost
Scarl

3

Câu trả lời của tôi về cơ bản chỉ là một phiên bản được đánh bóng, xác nhận đầy đủ và được đóng gói đầy đủ, câu trả lời của @ AlixAxel:

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

Thay đổi:

  • Nó đơn giản hóa tên hàm (với kiểu định dạng 'camelCase').

  • Nó bao gồm một kiểm tra để đảm bảo rằng chức năng chưa được khai báo trong một phần khác của mã của bạn.

  • Nó tính đến khả năng tương thích 'CloudFlare'.

  • Nó khởi tạo nhiều tên biến "liên quan đến IP" thành giá trị được trả về của hàm 'getClientIP'.

  • Nó đảm bảo rằng nếu hàm không trả về địa chỉ IP hợp lệ, tất cả các biến được đặt thành một chuỗi trống, thay vì null.

  • Đó chỉ là (45) dòng mã.


2

Câu hỏi lớn nhất là cho mục đích gì?

Mã của bạn gần như toàn diện như có thể - nhưng tôi thấy rằng nếu bạn phát hiện ra cái gì trông giống như một tiêu đề được thêm vào proxy, bạn sử dụng INSTEAD của CLIENT_IP, tuy nhiên nếu bạn muốn thông tin này cho mục đích kiểm toán thì rất dễ bị cảnh báo để giả.

Chắc chắn bạn không bao giờ nên sử dụng địa chỉ IP cho bất kỳ loại xác thực nào - ngay cả những địa chỉ này có thể bị giả mạo.

Bạn có thể có được một phép đo tốt hơn về địa chỉ IP của máy khách bằng cách đẩy ra một applet flash hoặc java kết nối lại với máy chủ thông qua một cổng không http (do đó sẽ tiết lộ các proxy hoặc trường hợp trong đó các tiêu đề được tiêm proxy là sai - nhưng Hãy nhớ rằng, nơi khách hàng CHỈ có thể kết nối qua proxy web hoặc cổng gửi đi bị chặn, sẽ không có kết nối nào từ applet.

C.


Cân nhắc tôi đang tìm kiếm một giải pháp duy nhất cho PHP, bạn có gợi ý tôi thêm $_SERVER['CLIENT_IP']như một câu lệnh if thứ hai không?
Corey Ballou

Không - chỉ là nếu bạn muốn đặt bất kỳ ý nghĩa nào lên dữ liệu được trả về, thì nên giữ địa chỉ điểm cuối mạng (IP khách) cũng như mọi thứ gợi ý giá trị khác trong các tiêu đề được thêm vào proxy (ví dụ: bạn có thể xem nhiều địa chỉ 192.168.1.x nhưng đến từ các ips khách khác nhau) C.
symcbean

1

Tôi nhận ra có nhiều câu trả lời hay và ngắn gọn hơn ở trên, và đây không phải là một chức năng cũng không phải là kịch bản duyên dáng nhất xung quanh. Trong trường hợp của chúng tôi, chúng tôi cần xuất cả x_forwarded_for giả mạo và remote_addr đáng tin cậy hơn trong một chuyển đổi đơn giản mỗi lần nói. Nó cần phải cho phép khoảng trống để tiêm vào các chức năng khác nếu-none hoặc if-singular (thay vì chỉ trả về chức năng được định dạng trước). Nó cần một var "bật hoặc tắt" với (các) nhãn tùy chỉnh cho mỗi chuyển đổi cho các cài đặt nền tảng. Nó cũng cần một cách để $ ip được động tùy theo yêu cầu để nó có dạng chuyển tiếp_for.

Ngoài ra, tôi không thấy bất cứ ai có địa chỉ isset () so với Hãy nhớ rằng bạn có thể giả mạo các từ như "PWNED" là x_forwarded_for để đảm bảo bạn khử trùng theo cú pháp ip thực sự nếu đầu ra của bạn được bảo vệ hoặc vào DB.

Ngoài ra, bạn có thể kiểm tra bằng google dịch nếu bạn cần đa proxy để xem mảng trong x_forwarder_for. Nếu bạn muốn kiểm tra các tiêu đề giả mạo, hãy kiểm tra tiện ích mở rộng giả mạo tiêu đề Chrome Client này . Điều này sẽ mặc định chỉ là remote_addr tiêu chuẩn trong khi đằng sau proxy anon.

Tôi không biết bất kỳ trường hợp nào mà remote_addr có thể trống, nhưng đó là trường hợp dự phòng.

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

Để tạo các động này để sử dụng trong (các) chức năng hoặc truy vấn / echo / khung nhìn bên dưới, hãy nói về nhật ký gen hoặc báo cáo lỗi, sử dụng toàn cầu hoặc chỉ lặp lại em ở bất cứ nơi nào bạn muốn mà không tạo ra hàng tấn điều kiện khác hoặc đầu ra lược đồ tĩnh chức năng.

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

Cảm ơn bạn cho tất cả những suy nghĩ tuyệt vời của bạn. Xin vui lòng cho tôi biết nếu điều này có thể tốt hơn, vẫn còn mới đối với các tiêu đề này :)


1

Tôi đã đưa ra chức năng này không chỉ đơn giản là trả về địa chỉ IP mà là một mảng có thông tin IP.

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

Đây là chức năng:

/**
 * 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.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}

1

Như ai đó đã nói trước đây, chìa khóa ở đây là vì lý do gì bạn muốn lưu trữ ips của người dùng.

Tôi sẽ đưa ra một ví dụ từ một hệ thống đăng ký mà tôi làm việc và tất nhiên giải pháp chỉ để đóng góp sth trong cuộc thảo luận cũ này thường xuyên xuất hiện trong các tìm kiếm của tôi.

Nhiều thư viện đăng ký php sử dụng ip để điều tiết / khóa các lần thử thất bại dựa trên ip của người dùng. Xem xét bảng này:

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

Sau đó, khi người dùng cố gắng đăng nhập hoặc bất cứ điều gì liên quan đến dịch vụ như đặt lại mật khẩu, một chức năng được gọi khi bắt đầu:

public function isBlocked() {
      /*
       * used one of the above methods to capture user's ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

Ví dụ, giả sử, $this->token->get('attempts_before_ban') === 10và 2 người dùng đến cùng một ips như trường hợp trong các mã trước đó , nơi các tiêu đề có thể bị giả mạo , sau đó sau 5 lần thử, cả hai đều bị cấm ! Thậm chí tệ nhất, nếu tất cả đều đến từ cùng một proxy thì chỉ có 10 người dùng đầu tiên sẽ được đăng nhập và tất cả những người còn lại sẽ bị cấm!

Điều quan trọng ở đây là chúng ta cần một chỉ mục duy nhất trên bảng attemptsvà chúng ta có thể lấy nó từ một kết hợp như:

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

trong đó jwt_loadxuất phát từ cookie http theo công nghệ mã thông báo web json nơi chúng tôi chỉ lưu trữ tải trọng được mã hóa nên chứa giá trị tùy ý / duy nhất cho mỗi người dùng. Tất nhiên yêu cầu nên được sửa đổi thành: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"và lớp cũng nên bắt đầu a private $jwt.


0

Tôi tự hỏi liệu có lẽ bạn nên lặp lại HTTP_X_FORWARDED_FOR theo thứ tự ngược lại, vì kinh nghiệm của tôi là địa chỉ IP của người dùng kết thúc ở cuối danh sách được phân tách bằng dấu phẩy, vì vậy bắt đầu từ đầu tiêu đề, bạn nhiều khả năng nhận được địa chỉ IP của một trong những proxy được trả lại, có khả năng vẫn có thể cho phép chiếm quyền điều khiển phiên vì nhiều người dùng có thể truy cập proxy đó.


1
Đã đọc trang wikipedia trên HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-For ... Tôi thực sự thấy thứ tự được đề xuất là từ trái sang phải vì mã của bạn có. Tuy nhiên, từ nhật ký của chúng tôi, tôi có thể thấy có rất nhiều trường hợp điều này không được các proxy trong tự nhiên tôn trọng và địa chỉ IP mà bạn muốn kiểm tra có thể ở cuối danh sách.
Chris Withers

1
Hoặc ở giữa, như sẽ xảy ra nếu một số proxy tôn trọng trật tự từ trái sang phải và những người khác thì không.
Brilliand

0

Cảm ơn vì điều này, rất hữu ích.

Nó sẽ giúp nếu mã đúng về mặt cú pháp. Vì nó có {quá nhiều xung quanh dòng 20. Điều tôi sợ có nghĩa là không ai thực sự thử điều này.

Tôi có thể bị điên, nhưng sau khi thử nó trên một vài địa chỉ hợp lệ và không hợp lệ, phiên bản duy nhất của validate_ip () hoạt động là:

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }

0

Đây là phiên bản sửa đổi nếu bạn sử dụng Dịch vụ của bộ đệm ẩn CloudFlare

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}

0

Chỉ là một phiên bản VB.NET của câu trả lời:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function

3
Có thẻ "PHP" trong câu hỏi
luchaninov 16/2/2016

0

Chỉ là một cách sạch sẽ khác:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }

0

Từ lớp Yêu cầu của Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}

Tài sản không xác định: $ server
C47

0

Tôi ngạc nhiên không ai nhắc đến bộ lọc_input, vì vậy đây là câu trả lời của Alix Axel cô đọng thành một dòng:

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}

-1

Bạn đã trả lời khá nhiều câu hỏi của riêng bạn! :)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

Nguồn


-6
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.