6

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

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

    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 不匹配。

4

5 回答 5

2

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

子网可以是巨大的(比如,在大学里)。

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

您可以交叉手指尝试 php ipv6在 PHP 中使用 IPv6 地址,甚至更聪明地尝试 mac address如何在 PHP 中获取连接客户端的 MAC 和 IP 地址?但两者都注定要失败。我的直觉是尝试作弊:我敢打赌,最好的方法基本上是使用网络共享作为会话存储,并允许负载平衡的 PHP 服务器全部访问它并通过相同的 dns 前缀执行所有操作。或者也许设置一个 3rd 方 dns 来进行会期聚会。

答案是,除非您像广告代理一样使用“cookies”进行跟踪,否则您无法做到这一点。

于 2012-10-29T21:16:13.847 回答
2

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

什么是代理

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

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

不同的解决方案

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

索引.php

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

ajax/username_check.php

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

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

比使用上面的代码更可靠、更简单的解决方案 ><

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

如果您无法在您所在的特定计算机上使用会话,您可以设置另一台计算机,重写会话处理程序(使用 php 提供的回调),或者不使用文件系统,而是使用数据库之类的其他东西进行会话? 您的网站必须使用一些会话也可以使用的东西。例如,如果您有一个基于负载平衡器文件的会话,通常无论如何都不会工作,所以通常最好将会话更改为使用我上面提到的其他东西。

于 2012-10-25T20:33:39.143 回答
1

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

修理:-

后:

foreach ($UserIPArray as $IPtoCheck) {

添加这一行:

$IPtoCheck = trim($IPtoCheck);

排序。

于 2012-10-26T13:09:23.637 回答
0

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

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

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

该字段的一般格式为:

X-Forwarded-For: client, proxy1, proxy2

其中值是一个逗号+空格分隔的IP地址列表,最左边的是原始客户端,每个传递请求的连续代理添加它接收请求的IP地址。在此示例中,请求通过了 proxy1、proxy2 和 proxy3(proxy3 显示为请求的远程地址)。

因此,您应该将explode分隔符更改为“,”,或者更好,使用preg_split“,\s*”作为分隔符并涵盖这两种情况。

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

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

于 2012-10-30T23:33:48.727 回答
0

我想讨论一下您的解决方案是否代表安全性。

$_SERVER['REMOTE_ADDR'] 不能被伪造。它是由网络服务器设置的,因为使用的访问 IP 地址。任何响应都会发送到该地址。

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

基于 IP 地址 FORGED 进行过滤不会真正帮助您,但这在很大程度上取决于您想要实现的目标 - 在您深入了解更多细节之前,这仍然是未知的。

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

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

HTTP 用户代理虽然是一个很好的独特信息来源。是的,它可以被伪造,但这并不重要,因为如果你只想允许来自同一来源的请求,那么假设用户代理字符串不会随着时间而改变是有效的,但是对于一个一堆其他用户。因此,有统计数据表明,如果您只查看所有发送的 HTTP 标头,您可以生成一个几乎唯一的浏览器指纹,因为每个用户都安装了不同的扩展来改变某些东西,比如接受内容标头。

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

于 2012-10-31T20:36:22.127 回答