PHP에서 사용자의 올바른 IP 주소를 검색하는 가장 정확한 방법은 무엇입니까?


301

IP 주소 검색에 사용할 수 있는 많은 $ _SERVER 변수 헤더 가 있다는 것을 알고 있습니다 . 위의 변수를 사용하여 사용자의 실제 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
이것을 구현할 때 이것을 명심하십시오 : stackoverflow.com/questions/1672827/…
Kevin Peno

19
이 모든 HTTP 헤더는 실제로 수정하기 쉽다는 것을 기억하십시오. 솔루션을 사용하면 임의의 IP로 X-Forwarded-For 헤더를 보내도록 브라우저를 구성하면 스크립트가 행복하게 가짜 주소를 반환합니다. 따라서 수행하려는 작업에 따라이 솔루션은 단순히 REMOTE_ADDR을 사용하는 것보다 안정성이 떨어질 수 있습니다.
gnomnain

14
OMFG, "믿을 수없는 IP"! 처음에는 SO에서 이와 같은 말도 안됩니다. 신뢰할 수있는 유일한 IP 주소는 REMOTE_ADDR입니다.
Common Sense

3
-1 스푸핑에 취약합니다. IP 주소가 무엇인지 사용자에게 묻기 만하면됩니다.
rook

답변:


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_varIP 주소에서 해킹되지 않은 int 검사를 제거 하므로 수정에 감사드립니다 . 또한 IPv6 주소의 유효성을 검사하는 옵션도 제공합니다. HTTP_X_FORWARDED_FOR최적화도 많이 감사합니다. 몇 분 안에 코드를 업데이트하겠습니다.
Corey Ballou

33
-1 이것은 당신이하는 모든 것을 스푸핑에 취약합니다. 사용자에게 자신의 IP 주소가 무엇인지 묻는 것입니다.
rook

7
@ 루크 : 예, 알아요. OP는 그것을 알고 있으며 내 대답에서 언급했습니다. 그러나 의견에 감사드립니다.
Alix Axel

1
참고 : Alix Axel의 코드가 작동하려면 FILTER_FLAG_IPV6을 제거해야했습니다.
darkAsPitch

2
@ rubenrp81 TCP 소켓 핸들러는 유일한 표준 소스이며 다른 모든 것은 공격자가 제어합니다. 위의 코드는 공격자의 꿈입니다.
rook

12

그럼에도 불구하고 사용자의 실제 IP 주소를 얻는 것은 신뢰할 수 없습니다. 익명 프록시 서버 (http_x_forwarded_for, http_forwarded 등의 헤더를 따르지 않는 서버) 만 사용하면 프록시 서버의 IP 주소 만 있으면됩니다.

그런 다음 익명의 프록시 서버 IP 주소 목록이 있는지 확인할 수 있지만 100 % 정확한지 확인할 수있는 방법은 없으며 프록시 서버임을 알 수 있습니다. 그리고 누군가가 영리하다면 HTTP 전달을 위해 헤더를 스푸핑 할 수 있습니다.

내가 지역 대학을 좋아하지 않는다고 가정 해 봅시다. 나는 당신이 어떤 IP 주소를 등록했는지 알아 내고, 당신이 HTTP 전달을 존중한다는 것을 알기 때문에 나쁜 일을함으로써 귀하의 사이트에서 그들의 IP 주소가 금지되도록합니다. 목록은 끝이 없습니다.

그리고 당신이 짐작했듯이, 내가 전에 언급했던 대학 네트워크와 같은 내부 IP 주소가 있습니다. 많은 것은 10.xxx 형식을 사용합니다. 공유 네트워크를 위해 전달되었다는 것입니다.

그런 다음에는 그다지 시작하지 않지만 동적 IP 주소는 더 이상 광대역의 길입니다. 그래서. 사용자 IP 주소를 얻더라도 가장 2-3 개월 안에 변경 될 것으로 예상하십시오.


입력 해 주셔서 감사합니다. 현재 클래스 IP IP를 제한 요소로 사용하여 세션 하이재킹을 제한하지만 이유 내에서 동적 IP를 허용함으로써 세션 인증을 지원하기 위해 사용자의 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 주소를 감지하는 이상한 문제 때문입니다 .


우린 방금 당신이 기본적으로 폭발하는 것과 같은 일을한다는 것을 깨달았습니다. 게다가 약간의 추가. 그래서 나는 내 대답이 많은 도움이 된 것 같지 않습니다. :)
gabrielk

로컬 호스트의 주소를 반환합니다
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'호환성을 고려합니다.

  • 'getClientIP'함수의 여러 "IP 관련"변수 이름을 리턴 값으로 초기화합니다.

  • 함수가 유효한 IP 주소를 반환하지 않으면 모든 변수가 대신 빈 문자열로 설정됩니다 null.

  • 단지 45 줄의 코드입니다.


2

가장 큰 문제는 어떤 목적을위한 것입니까?

귀하의 코드는 거의 포괄적입니다.하지만 프록시 추가 헤더처럼 보이는 것을 발견하면 CLIENT_IP의 INSTEAD를 사용하지만 감사 목적 으로이 정보를 원할 경우 매우 쉽습니다. 속이기 위해.

확실히 어떤 종류의 인증에도 IP 주소를 사용해서는 안됩니다. 스푸핑 될 수도 있습니다.

http가 아닌 포트를 통해 서버에 다시 연결되는 플래시 또는 자바 애플릿을 밀어 내면 클라이언트 IP 주소를 더 잘 측정 할 수 있습니다. 클라이언트가 웹 프록시를 통해서만 연결할 수 있거나 나가는 포트가 차단 된 경우 애플릿에서 연결되지 않습니다.

씨.


PHP 전용 솔루션을 찾고 있는데 $_SERVER['CLIENT_IP']두 번째 else if 문을 추가 할 것을 제안 하고 있습니까?
Corey Ballou

아니요-반환 된 데이터에 의미를 두려면 네트워크 추가 지점 주소 (클라이언트 IP)와 프록시 추가 헤더에서 다른 값을 제안하는 것을 유지하는 것이 좋습니다 (예 : 192.168.1.x 주소가 많이 있지만 다른 클라이언트 IP에서 오는 주소를 참조하십시오.) 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로 스푸핑 할 수 있으므로 출력이 보호되거나 DB로 출력되는 경우 실제 IP 구문으로 멸균해야합니다.

또한 x_forwarder_for에서 배열을보기 위해 다중 프록시가 필요한 경우 Google Translate를 사용하여 테스트 할 수 있습니다. 테스트하기 위해 스푸핑 헤더를 원한다면 Chrome 클라이언트 헤더 스푸핑 확장 프로그램을 확인 하세요 . 이것은 기본 프록시 뒤에있는 표준 remote_addr로 기본 설정됩니다.

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

로그 생성 또는 오류보고와 같이 아래의 함수 또는 쿼리 / 에코 / 뷰에 이러한 동적을 사용하려면 다른 조건이나 정적 스키마 출력을 만들지 않고 전역을 사용하거나 원하는 곳 어디에서나 em을 에코하십시오. 기능.

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

누군가 이전에 말했듯이, 여기서 중요한 것은 사용자의 IP를 저장하려는 이유입니다.

필자는 내가 작업하는 등록 시스템과 예를 들어 내 검색에서 자주 나오는이 오래된 토론에 sth를 기여하는 솔루션을 제시 할 것입니다.

많은 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 명의 사용자가 동일한 IP를 사용 한다고 가정하면 5 회 시도 후에 각각 이 금지됩니다. ! 최악의 경우, 모두 동일한 프록시에서 온 경우 처음 10 명의 사용자 만 기록되고 나머지는 모두 금지됩니다!

여기서 중요한 것은 테이블에 고유 인덱스가 필요하며 attempts다음과 같은 조합에서 얻을 수 있다는 것입니다.

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

여기서 json 웹 토큰 기술 jwt_load을 따르는 http 쿠키 에서 비롯되며 모든 사용자에 대해 임의의 고유 값을 포함 해야하는 암호화 된 페이로드 만 저장합니다 . 물론 요청을 다음 과 같이 수정해야하며 클래스도를 시작해야합니다 ."SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"private $jwt


0

내 경험에 따르면 사용자의 IP 주소가 쉼표로 구분 된 목록의 끝에서 끝나므로 헤더의 시작 부분부터 시작하므로 폭발 된 HTTP_X_FORWARDED_FOR을 역순으로 반복 해야하는지 궁금합니다. 프록시 중 하나의 IP 주소를 반환 할 가능성이 높으며이 프록시를 통해 많은 사용자가 세션 하이재킹을 허용 할 수 있습니다.


1
HTTP_X_FORWARDED_FOR에서 wikipedia 페이지를 읽은 후 : en.wikipedia.org/wiki/X-Forwarded-For ... 제안 된 순서는 실제로 코드의 왼쪽에서 오른쪽입니다. 그러나 로그에서 야생의 프록시가 이것을 존중하지 않는 경우가 많으며 확인하려는 IP 주소가 목록의 양쪽 끝에있을 수 있음을 알 수 있습니다.
Chris Withers

1
또는 중간에 프록시 중 일부가 왼쪽에서 오른쪽 순서를 존중하고 다른 프록시가 그렇지 않은 경우 발생할 수 있습니다.
화려한

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

정의되지 않은 속성 : $ server
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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.