关于ip地址:使用PHP通过代理获取IPAddress?

Getting IPAddress via a proxy with PHP?

我对许多AJAX调用运行安全检查,以查看我记录的请求是否有相同的IP。

我使用以下一组类函数来建立IP(可以通过负载平衡器来实现,因此是长时间的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    private function IPMask_Match ($network, $ip) {
      $ip_arr = explode('/', $network);
      if (count($ip_arr) < 2) {
        $ip_arr = array($ip_arr[0], null);
      }
      $network_long = ip2long($ip_arr[0]);
      $x = ip2long($ip_arr[1]);
      $mask =  long2ip($x) == $ip_arr[1] ? $x : 0xffffffff << (32 - $ip_arr[1]);
      $ip_long = ip2long($ip);
      return ($ip_long & $mask) == ($network_long & $mask);
    }


    private function IPCheck_RFC1918 ($IP) {
      $PrivateIP = false;
      if (!$PrivateIP) {
        $PrivateIP = $this->IPMask_Match('127.0.0.0/8', $IP);
      }
      if (!$PrivateIP) {
        $PrivateIP = $this->IPMask_Match('10.0.0.0/8', $IP);
      }
      if (!$PrivateIP) {
        $PrivateIP = $this->IPMask_Match('172.16.0.0/12', $IP);
      }
      if (!$PrivateIP) {
        $PrivateIP = $this->IPMask_Match('192.168.0.0/16', $IP);
      }
      return $PrivateIP;
    }


    public function getIP () {
      $UsesProxy = (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) || !empty($_SERVER['HTTP_CLIENT_IP'])) ? true : false;
      if ($UsesProxy && !empty($_SERVER['HTTP_CLIENT_IP'])) {
        $UserIP = $_SERVER['HTTP_CLIENT_IP'];
      }
      elseif ($UsesProxy && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $UserIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
        if (strstr($UserIP, ',')) {
          $UserIPArray = explode(',', $UserIP);
          foreach ($UserIPArray as $IPtoCheck) {
            if (!$this->IPCheck_RFC1918($IPtoCheck)) {
              $UserIP = $IPtoCheck;
              break;
            }
          }
          if ($UserIP == $_SERVER['HTTP_X_FORWARDED_FOR']) {
            $UserIP = $_SERVER['REMOTE_ADDR'];
          }
        }
      }
      else{
        $UserIP = $_SERVER['REMOTE_ADDR'];
      }
      return $UserIP;
    }

问题是我一直在通过代理操作的用户遇到问题。 任何人都可以说明为什么会这样吗? 我已经使用基本的免费代理在线尝试和模拟,但它看起来并没有获得可变的IP或任何东西 - 所以我不知道为什么这会说这两个IP不匹配。


遗憾的是,问题几乎肯定不是代理 - 它几乎可以肯定是固定的公共IP路由器,通过子网路由流量。

子网可能是巨大的(例如,在大学)。

即使它是一些真正的代理人(他们现在很少见 - 10岁的技术),即使代理志愿者转发,它也不会发生任何意义,因为它几乎肯定是像192.168这样的子网ip。 xx无论如何..这基本上是公共IP地址(又称交换机)内部扩展。

您可以交叉手指尝试php ipv6在PHP中使用IPv6地址甚至更聪明并尝试使用mac地址如何在PHP中获取已连接客户端的MAC和IP地址?但两者都注定要失败。我的直觉是试图欺骗:我会赌博,最好的方法是基本上使用网络共享的会话存储,并允许负载平衡的PHP服务器所有访问它,并通过相同的DNS前缀做一切。或者也许是为了进行会期聚会而设立第三方dns。

答案是,除非您像广告代理商那样跟踪"Cookie",否则您无法做到。


我将首先解释代理是什么,所以我们都在同一页面上。

什么是代理

代理通常是单个计算机,用户访问互联网,然后代理将结果发送回用户。当可能有数百甚至数千名其他人也在使用那台计算机时出现问题 - 他们都拥有相同的IP地址,但通常标题表示用户是通过代理。

我假设你的脚本(没有正确查看)正在混淆IP和标题。

不同解决方案

有一个更好的办法。使用会话并在会话中保存密钥,确保他们在访问ajax页面之前先访问过主站点。例:

的index.php

1
2
session_start();
$_SESSION['ajax_ok'] = true;

AJAX / username_check.php

1
2
3
4
session_start();
if (empty($_SESSION['ajax_ok'])) {
    die("You can not access this page...");
}

这将强制他们必须首先访问您的主站点,并且客户端还必须支持大多数浏览器将有助于防止机器人滥用您的ajax脚本的会话。

比使用上面提到的那些代码> <更可靠,更容易的解决方案

如果你不能使用会话怎么办?

如果你不能在你所使用的特定组件上使用会话,你可以设置另一台计算机,重写会话处理程序(使用php提供的回调)或者不使用文件系统而是使用其他类似数据库的会话来代替会话?您的网站必须使用会话也可以使用的内容。例如,如果你有一个基于负载均衡器文件的会话通常不会工作,所以通常一个好主意是改变会话以使用像我上面提到的其他内容。


一位朋友发现了这一点,基本上一些代理可以用X_FORWARDED_FOR作为逗号分隔值或以逗号分隔和间隔返回。

修理:-

后:

1
foreach ($UserIPArray as $IPtoCheck) {

添加此行:

1
$IPtoCheck = trim($IPtoCheck);

排序。


我想讨论一下你的解决方案是否代表安全做了什么。

$ _SERVER ['REMOTE_ADDR']无法伪造。它由Web服务器设置,因为使用了访问的IP地址。任何回复都会转到此地址。

$ _SERVER ['HTTP_FORWARDED_FOR']和$ _SERVER ['HTTP_CLIENT_IP']可以很容易伪造,因为它们是发送到网络服务器的HTTP标头 - 如果它被配置为省略这些标题,你也不知道你正在与代理通话,也不是如果客户决定插入这些标题,您是否知道您不与代理通话?

基于IP地址FORGED进行过滤并不会对您有所帮助,但这在很大程度上取决于您想要实现的内容 - 除非您在此处详细介绍,否则这些内容仍然未知。

如果您环顾四周,您将偶然发现用于PHP的Suhosin补丁和扩展,并且它的功能是加密会话和cookie内容。加密密钥是通过使用一些静态密钥构建的,但是添加了诸如HTTP用户代理或请求IP地址的部分之类的内容 - 注意:用于实际发出请求的IP,即使用的代理,因为这是只有可靠的信息。

有人可能会争辩说,使用完整的IP地址并不是一个好主意,除非您知道您的用户不使用代理群集来更改多个请求的IP地址,因此通常只使用部分IP,或者根本不使用。

HTTP用户代理虽然是一个很好的独特信息来源。是的,它可以伪造,但这根本不重要,因为如果您只想允许来自同一源的请求,则假定用户代理字符串不随时间变化是有效的,但对于一堆其他用户。因此,有统计数据显示,如果您只查看发送的所有HTTP标头,则可以生成几乎唯一的浏览器指纹,因为每个用户都会安装不同的扩展名来更改某些内容,例如接受内容标头。

我无法提供工作代码,也无法从您的问题中判断出这个答案是否适用,但我建议不要使用IP信息,特别是如果它可以伪造的话。如果考虑IPv6,甚至在同一个接口上每个客户端都有多个活动地址,这甚至更有效,并且它们都是随机生成的,并且不太可能在以后再次发生。 (当然,如果您永远不会主持IPv6,这不适用,但在某些时候您将不再是用户。)


The Problem is I've been getting problems with users operating via a
proxy. Can anyone indicate why that might be? I've used basic free
proxy's online to try and emulate, but it doesn't look to be getting
variable IPs or anything - so I'm not sure why this would be saying
the two IPs don't match.

您的解析代码在逗号上爆炸HTTP_X_FORWARDED_FOR,但分隔符可能是"逗号空间"。如果发生这种情况,RFC 1918检查将失败。虽然一些代理不添加空间,但标准是使用它:

1
http://en.wikipedia.org/wiki/X-Forwarded-For

The general format of the field is:

1
X-Forwarded-For: client, proxy1, proxy2

where the value is a comma+space separated list of IP addresses, the
left-most being the original client, and each successive proxy that
passed the request adding the IP address where it received the request
from. In this example, the request passed proxy1, proxy2 and proxy3
(proxy3 appears as remote address of the request).

因此,您应该将explode分隔符更改为","或更好,使用带有", s *"的preg_split作为分隔符并涵盖两种情况。

然后,您的问题是验证在AJAX中调用AJAX调用本身的页面。

如果您不想使用基于cookie的会话,这是最好的方法,您可以尝试使用nonce执行此操作。也就是说,当您生成页面时,您发出一个唯一的ID并将其注入HTML代码,其中AJAX代码将恢复它并传递回AJAX servlet。后者将能够在Access-Control-Request中添加它,如此处详述,或者只是向请求添加更多数据。