如何与IPv4 PHP一起处理IPv6地址

如何与IPv4 PHP一起处理IPv6地址,php,mysql,ip,ipv6,Php,Mysql,Ip,Ipv6,我是一名iphone开发人员,刚接触网络开发人员,所以请耐心等待我目前正在使用MAMP进行本地测试 我的网站上有一个高度安全的部分。除了需要用户/通行证外,它还检查用户的IP。如果该帐户以前从未从该IP使用过,它会将该IP以及唯一ID添加到保留表中,并向用户发送电子邮件,要求用户确认从该位置访问其帐户 如果用户登录,并且他们的IP与我的“允许”表中与他们的用户ID关联的任何IP都不匹配,它将执行上述任务,并且他们将收到一封电子邮件 我用来生成他们单击的链接的url的代码看起来有点像这样: if

我是一名iphone开发人员,刚接触网络开发人员,所以请耐心等待
我目前正在使用MAMP进行本地测试

我的网站上有一个高度安全的部分。除了需要用户/通行证外,它还检查用户的IP。如果该帐户以前从未从该IP使用过,它会将该IP以及唯一ID添加到保留表中,并向用户发送电子邮件,要求用户确认从该位置访问其帐户

如果用户登录,并且他们的IP与我的“允许”表中与他们的用户ID关联的任何IP都不匹配,它将执行上述任务,并且他们将收到一封电子邮件

我用来生成他们单击的链接的url的代码看起来有点像这样:

if (preg_match('/^127./',$ip)) {
    // accessed from this machine
    $val_url = "http://localhost:8888/mywebsite/admin/aproove_ip.php?email=$admin_email&val=$hash";
}
else if (preg_match('/^192\.168./',$ip)) {
    // accessed form a local networked computer
    $val_url = "http://super.local:8888/mywebsite/admin/aproove_ip.php?email=$admin_email&val=$hash";
    // note super.local is my machine's address, 8888 is MAMP port
}
else {
    // accessed from the WWW
    $val_url = "http://www.mywebsite.com/admin/aproove_ip.php?email=$admin_email&val=$hash";
}
现在,在我的电脑上进行测试时,这已经非常有效了

然而,我决定(不要问为什么)从我的iPod Touch进行测试,在它发送给我的电子邮件中(验证IP),它给了我完整的在线地址,就好像它是从WWW访问的一样(即,两个Regex都不满意)。我查看了包含请求的保留表,请求的IP是:
fe80::da30:62ff:fe18:6681

我猜那是ipv6我需要知道的是:

  • 我是否应该期望ipv6 addressess在我的站点上线时出现
  • 我如何判断它是否是本地请求(比如我的正则表达式为192.168…和127…)

如果您希望确保不会发生IPv6连接,并且可以添加到您的apache配置中,我将非常感谢您在这方面的任何建议,因为我发现这确实令人困惑。但是大多数网络主机还不支持IPv6(是的,我知道,我们在2011年),所以人们甚至不太可能通过IPv6与您连接。您看到这一点的唯一原因是因为bonjour(使
.local
地址工作的原因)在IPv6上运行

如果您想让您的代码准备好IPv6,几乎不需要做任何更改,因为大多数IP PHP函数都可以在IPv4和IPv6上工作。我记得所做的唯一更改是将MySQL表中的varchar数据类型增加到IPv6地址的最大长度(
39

我不确定IPv6是否遵循与IPv4相同的规则,但我预计验证IPv6地址是否为本地地址会更加困难

编辑:


fe80::/10
似乎是本地链接地址,可能只需检查前4位数字即可。

要查看IPv6地址是否为本地地址,您必须知道本地使用的地址。IPv6不进行NAT(通常)。一些网络在内部使用ULA(唯一本地地址),但许多网络只在内部使用从ISP获得的地址

你必须考虑的一件事是,有些(现在大多数)使用隐私扩展。这意味着它们的IPv6地址将随时间而改变。这将导致您的表大量增长,并使用户一次又一次地重新验证。我认为您最好的选择是只存储子网(前64位)并匹配该子网

以防您不知道IPv6地址语法:地址使用十六进制数字,格式为ssss:ssss:ssss:nnnn:nnnn:nnnnnn:nnnn,其中s是子网,n是节点/主机。省略每个4位块中的前导零,多个0块替换为::once。所以
fe80::da30:62ff:fe18:6681
实际上是
fe80:0000:0000:0000:da30:62ff:fe18:6681
。我自己的Web服务器有地址
2001:4038:0:16::16
,它是
2001:4038:0000:0016:0000:0000:0000:0016
的缩写,子网是
2001:4038:0:16::/64

从地址获取子网的示例代码:

<?php

# Get the original IP address, for example from the command line
$original_ip_str = $argv[1];

# Converto to binary form (suppress errors, we handle them)
$original_ip_bin = @inet_pton($original_ip_str);
if ($original_ip_bin === FALSE) {
  $subnet_str = FALSE;
  $subnet_bin = FALSE;
} else {
  if (strlen($original_ip_bin) == 16) {
    # IPv6: Replace the last 64 bits with zeroes
    $subnet_bin = substr_replace($original_ip_bin, str_repeat("\000", 8), -8);
  }

  # Convert the result back to readable form (optional)
  $subnet_str = inet_ntop($subnet_bin);
}

# Show the result
echo "IPv6 address: $original_ip_str\n";
echo "IPv6 subnet: $subnet_str\n";

谢谢已经让我的varchar长了39个字符-我想我不应该真的担心bonjour,那只是为了测试,最后当它上线时,它会做我想做的事情,不管是不是IPv6!您不应该使用varchar来存储IP地址—无论是v4还是v6。它阻止您使用位掩码高效地查询它们,并浪费大量存储空间。请使用INET_ATON()和INET_NTOA()在MySQL中存储和检索(分别)IP地址。因为在一般应用程序中存储IP的大多数用途只是为了跟踪用户,而且由于MySQL不能证明是以可读方式存储IP的本机方法,我通常只是为了方便而使用varchar(特别是当它可能是IPv6或IPv4时)。只需澄清的是,
fe80::
fe10::
表示本地??AFAICT专用网络地址指定为fc00::/7,而不是fe。。。请参阅,您能否提供一些代码,以便仅从IPv6地址获取子网?-然后我将其代码存储在我的数据库中…当然!在答案+1中添加代码,以使您的代码符合IPv6!今天编写的太多程序仍然不支持IPv6,即使IPv4地址已经用完。