【问题标题】:How to handle IPv6 addresses alongside IPv4 PHP如何处理 IPv6 地址和 IPv4 PHP
【发布时间】:2011-11-09 13:35:03
【问题描述】:

我是一名 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 访问的一样(即两个正则表达式都不满意)。我查看了包含请求的保留表,请求的 IP 是:fe80::da30:62ff:fe18:6681

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

  • 我是否应该期望 ipv6 地址在我的网站上线时访问我的网站?
  • 如何判断它是否是本地请求(例如我的 192.168... 和 127... 的正则表达式)

我非常感谢任何关于这方面的建议,因为我觉得这真的很令人困惑

【问题讨论】:

  • +1 表示希望您的代码符合 IPv6 标准!即使 IPv4 地址已经用完,今天编写的太多程序仍然不支持 IPv6。

标签: php mysql ip ipv6


【解决方案1】:

如果您想确保不会发生 IPv6 连接,可以将 Listen 0.0.0.0:80 添加到您的 apache 配置中。但是大多数网络主机仍然不支持 IPv6(我知道,我们在 2011 年),所以人们甚至不太可能通过 IPv6 连接到您。您看到这一点的唯一原因是 bonjour(使 .local 地址工作的原因)在 IPv6 上运行。

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

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

编辑:

fe80::/10 似乎是本地链接地址,可能就像检查前 4 位数字一样简单。

【讨论】:

  • 谢谢! - 已经让我的 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...见en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses
【解决方案2】:

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

您必须考虑的一件事是,一些(大多数?现在)使用隐私扩展。这意味着它们的 IPv6 地址会随着时间而改变。这将导致您的表增长很多,并且会使用户一遍又一遍地重新进行身份验证。我认为您最好的选择是仅存储子网(前 64 位)并在其上进行匹配。

以防万一您不知道 IPv6 地址语法:地址使用十六进制数字,格式为 ssss:ssss:ssss:ssss:nnnn:nnnn:nnnn:nnnn 其中 s 是子网,n 是节点/主持人。每个4位数字块中的前导零被省略,多个0块被替换为::一次。所以fe80::da30:62ff:fe18:6681 实际上是fe80:0000:0000:0000:da30:62ff:fe18:6681。我自己的webserver地址是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";

【讨论】:

  • 您能否提供一些代码来从 IPv6 地址获取子网? - 然后我将其存储在我的数据库中...
  • 当然!答案中添加的代码
猜你喜欢
  • 2011-06-09
  • 2010-10-01
  • 2018-07-23
  • 2012-12-25
  • 1970-01-01
  • 2011-02-16
  • 2016-09-20
  • 2016-09-21
相关资源
最近更新 更多