Filter and tunnel network traffic with NetworkExtension
System Services 进阶 1m

用 NetworkExtension 过滤和隧道化网络流量

Filter and tunnel network traffic with NetworkExtension

2025年6月9日

在 Apple 官方观看视频

一句话判断

iOS 26 终于允许你根据完整 URL 而不只是 host 来做内容过滤了——但代价是你永远碰不到 URL 本身,系统用 Bloom filter + 同态加密 + Privacy Pass 做了一个隐私优先的方案。

这场 Session 讲了什么

Session 先回顾了 NetworkExtension 框架的全貌:Wi-Fi 管理、Local Push、DNS 配置、透明代理、网络中继(Network Relay)、IP VPN、内容过滤,然后重点介绍了 iOS 26 新增的 URL Filter API。

传统的内容过滤(NEFilterDataProvider)只能看到流量的 host 和 port,无法区分同一站点下的不同资源。新的 URL Filter 让你可以基于完整 URL 做过滤决策,但整个过程系统化执行:app 不在过滤路径中,无法接触任何流量数据。系统先用 Bloom filter 在设备端做快速预过滤,命中(可能在黑名单中)时再通过 Private Information Retrieval(PIR)向你的服务器发送加密查询做精确匹配。所有查询经过 Apple 托管的 OHTTP Relay,服务器无法得知查询内容和用户身份。

Session 还回顾了 VPN 相关的 API 选择:用 Network Relay(MASQUE 协议)做 TCP/UDP 隧道,用 NEVPNManager 做 IP 级 VPN,并强调了 enforceRoutesincludeAllNetworks 选项的正确用法。

值得深挖的点

URL Filter 的隐私架构

这套方案的核心矛盾是:你需要基于完整 URL 做过滤,但 URL 本身包含敏感信息(浏览历史、用户标识符等)。Apple 的解法是四层技术栈:Bloom filter 做设备端预过滤(零误报,有概率误报命中),PIR 做加密数据库查询(服务器只能看到加密查询),Privacy Pass 做匿名认证(防止服务器追踪用户),OHTTP Relay 隐藏客户端 IP。

从架构角度看,这是一种”系统做决策,app 提供数据”的模式。你的 app 只负责两件事:提供 Bloom filter 数据(通过 NEURLFilterControlProvider 扩展)和部署 PIR 服务器。过滤逻辑完全由系统执行,app 无法知道用户访问了哪些 URL。这和传统的 NEFilterDataProvider 模式有本质区别——后者虽然也不能修改流量,但可以看到流信息(host、port)。

Bloom filter 的误报率设计是一个 trade-off:filter 越大误报率越低,但设备端存储和内存开销越大。误报意味着需要更多的 PIR 查询,增加延迟和服务器负载。建议根据你的 URL 数据集规模选择合适的 Bloom filter 参数,文档中有具体的 hash 函数和构建方法说明。

OHTTP Relay 的审批流程

这个细节容易被忽略:URL Filter 的查询必须经过 Apple 托管的 OHTTP Relay。你需要先提交申请,Apple 会验证你的服务器配置(包括 OHTTP Gateway),批准后才能在 App Store/TestFlight/企业分发中使用。开发签名的 build 豁免此要求。

这意味着 URL Filter 不是一个随便接入的功能。Apple 作为隐私守护者的角色在这里体现得很明确:它控制着数据流经的每一个环节。对开发者来说,这是一个信号——如果你的内容过滤需求可以通过 host 级别的 NEFilterDataProvider 满足,不要急着迁移到 URL Filter,因为后者的准入门槛高得多。

代码片段

配置 URL Filter 的 app 端核心流程:

import NetworkExtension

func configureURLFilter() async throws {
    let manager = NEURLFilterManager.shared
    try await manager.loadFromPreferences()

    manager.urlFilterConfiguration?.pirServerURL = URL(string: "https://pir.example.com")!
    manager.urlFilterConfiguration?.pirPublicKey = pirPublicKeyData

    try await manager.saveToPreferences()
    // URL Filter 生效
}

实现 NEURLFilterControlProvider 提供 Bloom filter:

class FilterExtension: NSObject, NEURLFilterControlProvider {
    func fetchPrefilter() async throws -> Data {
        // 从 bundle 或服务器获取 Bloom filter 数据
        if let url = Bundle.main.url(forResource: "bloomfilter", withExtension: "bin"),
           let data = try? Data(contentsOf: url) {
            return data
        }
        // 或从服务器下载最新 Bloom filter
        return try await downloadBloomFilter()
    }
}

非 WebKit/URLSession 应用调用参与 API 检查 URL:

let verdict = try await NEURLFilter.verdict(for: request.url)
if verdict == .denied {
    // 拒绝该请求
    throw URLError(.resourceUnavailable)
}

最佳实践

建议优先使用 Network Relay(MASQUE)替代传统 VPN 来做企业远程访问。Network Relay 不需要你构建自己的扩展,通过 NERelayManager API 配置即可,且平台内置支持。

避免使用 NEPacketTunnelProvider 做内容过滤。它工作在 IP 层,拿不到应用层的流量信息。如果目标是过滤内容,请使用 Content Filter API。如果目标是 URL 过滤,请使用新的 URL Filter API。

对于 Bloom filter 数据集不经常变化的场景,把它打包在 app bundle 中,通过 app 更新来更新。对于频繁变化的数据集,配置系统定期触发扩展的 fetchPrefilter 方法从服务器下载。

还有什么值得关注

  • iOS 26 新增 NEHotspotHelper 扩展 API,用于 Wi-Fi 热点交互。
  • VPN 的 excludeLocalNetworks 等异常选项现在大多数默认开启,但可以根据安全需求自定义。
  • URL Filter 的 PIR 服务器有开源示例代码,包含完整的 PIR 服务器和 Privacy Pass Issuer 实现。
系统服务