Який найточніший спосіб отримати правильну IP-адресу користувача в PHP?


301

Я знаю, що існує велика кількість заголовків змінних $ _SERVER для пошуку IP-адрес. Мені було цікаво, чи існує загальний консенсус щодо того, як найбільш точно отримати реальну IP-адресу користувача (добре знаючи, що жоден метод не є ідеальним), використовуючи вказані змінні?

Я витратив деякий час, намагаючись знайти глибоке рішення, і придумав наступний код на основі ряду джерел. Мені б сподобалося, якби хтось міг попросити пробитися у відповіді або пролити трохи світла на щось, можливо, більш точне.

редагування включає оптимізації від @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;
 }

Слова попередження (оновлення)

REMOTE_ADDRяк і раніше є найбільш надійним джерелом IP-адреси. Інші $_SERVERзгадані тут змінні можуть бути легко підроблені віддаленим клієнтом. Метою цього рішення є спроба визначити IP-адресу клієнта, що сидить за проксі. Для загальних цілей ви можете скористатися цим у поєднанні з IP-адресою, повернутою безпосередньо з $_SERVER['REMOTE_ADDR']обох і зберігати.

Для 99,9% користувачів це рішення ідеально відповідає вашим потребам. Це не захистить вас від 0,1% зловмисних користувачів, які прагнуть зловживати вашою системою, вводячи власні заголовки запитів. Якщо ви покладаєтесь на IP-адреси для чогось важливого для місії, вдайтеся до REMOTE_ADDRта не переймайтесь обслуговуванням тих, хто стоїть за проксі-сервером.


2
Що стосується питання whatismyip.com, я думаю, що вони роблять щось подібне до цього сценарію, чи використовуєте ви його локально? Якщо у вас є внутрішній IP, у цьому випадку нічого не надсилається через загальнодоступний інтерфейс, тому для PHP немає інформації
Matt

2
Не забудьте це пам’ятати, виконуючи
Кевін Пено

19
Пам'ятайте, що всі ці заголовки HTTP насправді легко змінити: за допомогою вашого рішення мені просто потрібно налаштувати свій браузер для надсилання заголовка X-Forwarded-For з випадковим IP-адресом, і ваш сценарій з радістю поверне підроблену адресу. Отже, залежно від того, що ви намагаєтеся зробити, це рішення може бути менш надійним, ніж просто використання REMOTE_ADDR.
gnomnain

14
OMFG, "ненадійний ip"! Перший раз я бачу таку дурницю тут, на SO. Єдиною надійною IP-адресою є REMOTE_ADDR
Ваше загальне чуття

3
-1 це схильне до підробки. Все, що ви робите, - це запитувати користувача, якою має бути його ip-адреса.
грак

Відповіді:


268

Ось коротший і чіткіший спосіб отримати 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;
                }
            }
        }
    }
}

Я сподіваюся, що це допомагає!


Ваш код, здається, вже досить повний, я не бачу в ньому можливих помилок (окрім звичних застережень IP), я б змінив validate_ip() функцію, щоб покладатися на розширення фільтра, хоча:

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;
}

Також ваш HTTP_X_FORWARDED_FOR фрагмент можна спростити з цього:

// 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'];
    }
}

До цього:

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

Ви також можете перевірити IPv6 адреси.


4
Я безумовно вдячний за filter_varвиправлення, оскільки він видаляє купу хакейських непідписаних чеків на IP-адресу. Мені також подобається те, що він дає мені можливість перевірити IPv6 адреси. HTTP_X_FORWARDED_FORОптимізація також високо цінується. Через кілька хвилин я оновлю код.
Corey Ballou

33
-1 це вразливо для того, щоб підробляти все, що ви робите, це запитувати користувача, якою має бути його ip-адреса.
грак

7
@Rook: Так, я знаю. ОП це знає, і я це також згадував у своїй відповіді. Але дякую за коментар.
Алікс Аксель

1
FYI: Мені довелося видалити FILTER_FLAG_IPV6, щоб заставити код Alix Axel працювати.
darkAsPitch

2
@ rubenrp81 Обробник сокетів TCP - єдине канонічне джерело, все інше контролюється зловмисником. Код вище - мрія зловмисника.
грак

12

Однак навіть отримання реальної IP-адреси користувача стає ненадійним. Все, що їм потрібно зробити, - це використовувати анонімний проксі-сервер (той, який не шанує заголовки для http_x_forwarded_for, http_forwarded тощо), і все, що ви отримуєте, - це IP-адреса проксі-сервера.

Потім ви можете побачити, чи є список IP-адрес проксі-сервера, які є анонімними, але немає ніякого способу бути впевненим у тому, що це на 100% точність, і найбільше це зробить, - це вам знати, що це проксі-сервер. І якщо хтось розумний, він може підробляти заголовки для HTTP вперед.

Скажімо, мені не подобається місцевий коледж. Я розумію, які IP-адреси вони зареєстрували, і забороняю їх IP-адресу на вашому веб-сайті, роблячи погані речі, тому що я вважаю, що ви вшановуєте HTTP пересилаючих. Список нескінченний.

Тоді, як ви здогадалися, є внутрішні IP-адреси, такі як мережа коледжів, про яку я згадував раніше. Багато використовують формат 10.xxx. Отже, все, що ви знаєте, - це те, що він був пересланий для спільної мережі.

Тоді я не буду починати багато в цьому, але динамічні IP-адреси - це спосіб широкосмугового зв’язку. Тому. Навіть якщо ви отримаєте IP-адресу користувача, очікуйте, що вона зміниться за 2–3 місяці, найдовше.


Дякую за вклад. В даний час я використовую IP-адресу користувача, щоб допомогти в аутентифікації сеансу, використовуючи IP-клас класу C як обмежуючий фактор для обмеження викрадення сеансу, але дозволяючи динамічні IP-адреси в межах причини. Підроблені IP-адреси та анонімні проксі-сервери - це лише те, з чим мені доведеться мати справу для вибраної групи людей.
Corey Ballou

@cballou - Безумовно, для цього REMOTE_ADDR є правильним. Будь-який підхід, що спирається на заголовки HTTP, вразливий для підробки заголовка. Як довго триває сеанс? Динамічні IP-адреси не змінюються швидко.
MZB

Вони роблять, особливо якщо я хочу, щоб вони змінили mac-адресу, яку підтримує багато водіїв). Для цього достатньо лише REMOTE_ADDR, щоб отримати останній сервер, з яким він спілкувався. Отже, у ситуації проксі ви отримуєте IP проксі-сервера.

8

Ми використовуємо:

/**
 * 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'];
    }
}

Вибух на HTTP_X_FORWARDED_FOR відбувається через дивні проблеми, у яких ми виявляли IP-адреси, коли використовувався Squid .


Ну, щойно я зрозумів, що ти в основному робиш те саме, що вибухаєш і так далі. Плюс трохи зайвого. Тому я сумніваюся, що моя відповідь дуже допомогла. :)
gabrielk

Це повертає адресу localhost
Scarl

3

Моя відповідь - це лише відшліфована, повністю підтверджена та повністю упакована версія відповіді @ 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 = '';
    };

?>

Зміни:

  • Це спрощує назву функції (у стилі форматування 'camelCase').

  • Він включає перевірку, щоб переконатися, що функція вже не оголошена в іншій частині вашого коду.

  • Він враховує сумісність "CloudFlare".

  • Він ініціалізує кілька повернених імен змінних, пов'язаних з IP, функції "getClientIP".

  • Це гарантує, що якщо функція не повертає дійсну IP-адресу, всі змінні встановлюються на порожню рядок, а не на null.

  • Це лише (45) рядки коду.


2

Найбільше питання - з якою метою?

Ваш код майже настільки вичерпний, як міг би бути, - але я бачу, що якщо ви помітите те, що схоже на заголовок проксі, доданого, ви використовуєте ВСТАНОВИТИ CLIENT_IP, однак, якщо ви хочете, щоб ця інформація для аудиторських цілей була попереджена, це дуже просто підробляти.

Звичайно, ви ніколи не повинні використовувати IP-адреси для будь-якої автентифікації - навіть вони можуть бути підробленими.

Ви можете краще оцінити ip-адресу клієнта, натиснувши на нього аплету flash або java, який підключається назад до сервера через не-http-порт (який би виявляв прозорі проксі-сервери або випадки, коли проксі-введені заголовки помилкові - але майте на увазі, що там, де клієнт може ТІЛЬКИ підключитися через веб-проксі або вихідний порт заблокований, з'єднання з аплетом не буде.

C.


Враховуючи, що я шукаю рішення лише для PHP, ви пропонуєте мені додати $_SERVER['CLIENT_IP']як друге інше, якщо заява?
Corey Ballou

Ні - лише те, що якщо ви хочете помістити будь-яке значення на повернених даних, тоді було б непогано зберегти адресу кінцевої точки мережі (IP клієнта), а також все, що пропонує інше значення в заголовках проксі-сервера (наприклад, ви можете дивіться багато адрес 192.168.1.x, але надходять від ips різних клієнтів) C.
symcbean

1

Я розумію, що вище набагато кращі і стисліші відповіді, і це не функція, ані найграціозніший сценарій. У нашому випадку нам потрібно було вивести як підкачувальний x_forwarded_for, так і більш надійний remote_addr у спрощеному перемиканні на вимову Потрібно було дозволити пробіли для введення в інші функції if-none або if-singular (а не просто повернення попередньо відформатованої функції). Для налаштування платформи знадобився змінний режим "увімкнення або вимкнення" з налаштованою етикеткою (ими) за перемикач. Також потрібен був спосіб, щоб $ ip був динамічним, залежно від запиту, щоб він набув форми forwarded_for.

Крім того, я не бачив, щоб хто-небудь адресу isset () vs! Empty () - можна нічого не вводити для x_forwarded_for, але все-таки викликати isset () правду, що призводить до порожнього var, спосіб обійти це використання && та об'єднання обох як умов. Майте на увазі, що ви можете підробляти слова типу "PWNED" як x_forwarded_for, тому переконайтеся, що ви стерилізуєте справжній синтаксис ip, якщо ваш вихід десь захищений або в БД.

Також ви можете протестувати за допомогою google translate, якщо вам потрібен мультипроксі-сервер для перегляду масиву в x_forwarder_for. Якщо ви хочете протестувати заголовки підробляння, перевірте це в підставці заголовка клієнта Chrome розширенням . Це буде за замовчуванням просто стандартний remote_addr, а позаду anon проксі.

Я не маю на увазі будь-який випадок, коли remote_addr може бути порожнім, але його немає як резервний про всяк випадок.

// 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 = '';
}

Щоб зробити ці динамічними для використання у функціях (іх) або запиті / ехо / переглядах нижче, скажімо, для журналу про звітність про помилки чи помилки, використовуйте глобалі або просто повторюйте їх куди завгодно, не роблячи низку інших умов чи статичних схем-виходів функції.

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

Дякую за всі ваші чудові думки. Будь ласка, дайте мені знати, чи могло б це бути краще, все-таки новим для цих заголовків :)


1

Я придумав цю функцію, яка не просто повертає IP-адресу, а масив з 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;
}

Ось функція:

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

Як хтось говорив раніше, головне тут, з якої причини ви хочете зберігати ips користувача.

Наведу приклад із системи реєстрації, над якою працюю, і, звичайно, рішення просто внести щось у цю стару дискусію, яка часто буває в моїх пошуках.

Багато бібліотек реєстрації php використовують ip для вимикання / блокування невдалих спроб на основі ip користувача. Розглянемо цю таблицю:

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

Потім, коли користувач намагається виконати логін або що-небудь, пов’язане з обслуговуванням, таким як скидання пароля, на початку викликається функція:

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";
   }

Скажімо, наприклад, $this->token->get('attempts_before_ban') === 10і 2 користувачі отримують ті самі ips, як це було у попередніх кодах, де заголовки можна підробляти , після 5 спроб кожен обидва забороняється ! Навіть найгірше, якщо всі приходять з одного проксі, тоді буде введено лише перші 10 користувачів, а всі інші будуть заборонені!

Найважливішим тут є те, що нам потрібен унікальний індекс на таблиці, attemptsі ми можемо отримати його з комбінації типу:

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

звідки jwt_loadйде файл cookie http, який слід за технологією json web token, де ми зберігаємо лише зашифровані корисні навантаження, які повинні містити довільне / унікальне значення для кожного користувача. Звичайно, запит має бути змінено на: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"і клас також повинен ініціювати a private $jwt.


0

Мені цікаво, чи, можливо, вам слід повторити над підірваним HTTP_X_FORWARDED_FOR у зворотному порядку, оскільки мій досвід свідчить, що IP-адреса користувача закінчується в кінці списку, розділеного комами, тому, починаючи з початку заголовка, ви більша ймовірність отримати ip-адресу одного з проксі-серверів, що потенційно все ще може дозволити викрадення сеансу, оскільки через цей проксі-сервер може потрапити багато користувачів.


1
Прочитавши сторінку wikipedia на HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-Для ... Я бачу, запропонований порядок дійсно зліва направо, оскільки його код має. Однак, з наших журналів я бачу, що є чимало випадків, коли проксі-сервери не дотримуються цього, і IP-адреса, яку ви хочете перевірити, може бути в будь-якому кінці списку.
Кріс Вітерс

1
Або посередині, як би це сталося, якби деякі довірені особи дотримувалися порядку зліва направо, а інші - ні.
Brilliand

0

Дякую за це, дуже корисно.

Хоча це допоможе, якби код був синтаксично правильним. Так як це є {занадто багато навколо рядка 20. Що, боюся, означає, що ніхто насправді цього не пробував.

Я можу бути божевільним, але, випробувавши його на кількох дійсних та недійсних адресах, єдиною версією validate_ip (), яка працювала, була така:

    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

Ось модифікована версія, якщо ви використовуєте служби кешування 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

Просто VB.NET версія відповіді:

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
У питанні є тег "PHP"
luchaninov

0

Ще один чистий спосіб:

  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

З класу запиту 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);
}

Невизначена нерухомість: $ сервера
C47

0

Я здивований, що ніхто не згадав filter_input, тому ось відповідь Alix Axel зведена до однорядного :

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

Ви майже відповіли на власне запитання! :)

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;
}

Джерело


-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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.