---
title: "IP 白名单偶尔漏判：处理 CIDR 和 IPv4 映射地址"
date: 2026-11-13
slug: cidr-allowlist-ipv4-mapped-ipv6
canonical: https://kernelranch.com/blog/2026/11/cidr-allowlist-ipv4-mapped-ipv6
markdown_url: https://kernelranch.com/blog/2026/11/cidr-allowlist-ipv4-mapped-ipv6.md
tags: ["network", "cidr", "ipv6"]
---同一个 IPv4 对端可能以 `192.0.2.9` 或 `::ffff:192.0.2.9` 进入应用。先确定业务如何处理 IPv4 映射地址，再用 CIDR 成员关系判断。下面的策略把映射地址还原为 IPv4，白名单配置仍分别使用原生 IPv4 与 IPv6 网段。

## 1. 本机复现

保存为 `cidr_lab.py`，运行 `python cidr_lab.py`。地址只作匹配输入，脚本不发网络请求。

```python
from ipaddress import IPv6Address, ip_address, ip_network

networks = tuple(ip_network(value, strict=True) for value in (
    "192.0.2.0/28", "2001:db8:42::/48"
))

def allowed(value):
    try:
        address = ip_address(value)
    except ValueError:
        return False
    if isinstance(address, IPv6Address):
        if address.scope_id is not None:
            return False
        if address.ipv4_mapped is not None:
            address = address.ipv4_mapped
    return any(address.version == network.version and address in network
               for network in networks)

for value, expected in (
    ("192.0.2.15", True),
    ("192.0.2.16", False),
    ("::ffff:192.0.2.9", True),
    ("::192.0.2.9", False),
    ("2001:db8:42::1", True),
    ("2001:db8:43::1", False),
    ("192.0.2.9:443", False),
):
    result = allowed(value)
    assert result is expected
    print(f"{value} -> {result}")

try:
    ip_network("192.0.2.9/28", strict=True)
except ValueError:
    print("network with host bits: rejected")
else:
    raise AssertionError("expected strict network validation")
```

本地实测输出如下。

```text
192.0.2.15 -> True
192.0.2.16 -> False
::ffff:192.0.2.9 -> True
::192.0.2.9 -> False
2001:db8:42::1 -> True
2001:db8:43::1 -> False
192.0.2.9:443 -> False
network with host bits: rejected
```

## 2. 把对端来源和匹配规则分开配置

`/28` 固定 IPv4 的前 28 位，剩下 4 位给出 16 个地址。本例按前缀做成员匹配，因此 `.15` 仍在范围内。网络接口的广播地址、可分配地址等规则属于另一层。`strict=True` 会拒绝配置里的主机位，避免运营人员输入单个主机地址时无意中授权整个网段。

应用直连时，可以从连接元数据读取对端地址。部署在反向代理后，先确认哪一跳可信，以及该代理是否会覆盖应用读取的转发头。客户端自行提供的转发头不能直接作为白名单输入。地址与端口应从框架解析后的字段中分别取得，避免用冒号切割 IPv6。若决定把映射地址按 IPv4 处理，配置校验也应引导管理员用 IPv4 CIDR 表达对应规则。

## 小结

使用地址对象执行前缀匹配，明确映射地址的归类，再从可信连接链路获取待判定地址。

## 数据来源与检索时间

检索时间 2026-11-13。

| 来源 | URL | 对应内容 |
|---|---|---|
| RFC 4632 第 3.1 节 | https://www.rfc-editor.org/rfc/rfc4632.html#section-3.1 | CIDR 前缀表示与地址数 |
| RFC 4291 第 2.5.5.2 节 | https://www.rfc-editor.org/rfc/rfc4291.html#section-2.5.5.2 | IPv4 映射 IPv6 地址格式 |
| Python ipaddress | https://docs.python.org/3.11/library/ipaddress.html | 网段严格校验、成员关系与 ipv4_mapped |

## 核验说明

- Windows、Python 3.11.9 验证了 IPv4 边界、IPv6、映射地址、错误输入和带主机位的配置。真实代理链与白名单中间件需要结合部署另测。
