Highlight
Apple 在 iOS 17 和 macOS Sonoma 中提供了一组新的网络中继(Network Relay)API,开发者可以用 MASQUE 和 Oblivious HTTP 协议为 App 的网络流量添加隐私保护层,企业管理员可以用它替代传统 VPN 实现员工对内部资源的安全访问。
核心内容
问题:为什么 VPN 不是所有场景的最佳答案
对于需要保护网络流量的开发者来说,传统方案很有限。要么什么都不做,让用户的 IP 地址暴露给服务器;要么接入 VPN,但 VPN 有几个固有问题:
- 范围太大:VPN 通常接管整台设备的所有流量,App 无法只为自己关心的请求开启保护。
- 用户体验差:VPN 连接慢、切换网络时需要重新连接、用户需要手动开关。
- 信任模型不理想:VPN 服务商能看到用户的真实 IP 和目标访问地址,相当于把隐私交给了一个中间人。
对于企业场景,VPN 配置和分发对 IT 团队来说也是个负担。员工需要安装 VPN 客户端、输入配置、处理掉线重连。
Apple 的方案:网络中继
Apple 在 iOS 17 / macOS Sonoma 中引入了网络中继(Network Relay)的支持。中继是一种特殊类型的代理服务器,基于 IETF 标准化的两个协议:
- MASQUE:支持代理任意 TCP 和 UDP 连接,不需要修改后端服务器。
- Oblivious HTTP:专为 HTTP 流量设计,适合 REST API 场景。
中继的核心优势是可以链接(chain)。你可以配置两个中继服务器:第一个中继知道用户的 IP 但不知道访问内容(内容已加密),第二个中继知道访问内容但不知道用户 IP。没有任何单一方能同时获得两者。这正是 iCloud Private Relay 背后的技术。
对开发者来说,这意味着你可以为自己的 App 单独配置中继,不影响设备上的其他流量。对企业来说,中继比 VPN 更轻量——连接更快、只代理指定域名的流量、可以和现有身份提供商集成。
详细内容
在 App 中配置单个中继
配置一个中继只需要几行代码。在 Network framework 中创建一个 ProxyConfiguration 对象即可(04:52):
import Network
let relayEndpoint = NWEndpoint.url(URL(string: "https://relay.example.com")!)
let relayServer = ProxyConfiguration.RelayHop(http3RelayEndpoint: relayEndpoint)
let relayConfig = ProxyConfiguration(relayHops: [relayServer])
关键点:
NWEndpoint.url接受中继服务器的 URL,协议必须是 HTTPSRelayHop代表中继链路中的一跳,http3RelayEndpoint指定使用 HTTP/3 协议与中继通信ProxyConfiguration的relayHops是数组,支持单跳或多跳(chain)- 上述代码只创建了配置对象,还没有应用到任何连接上
三种使用方式:Network framework、URLSession、WebKit
中继配置可以注入到 Apple 的三个主要网络 API 中。
方式一:Network framework(05:40)
import Network
let relayEndpoint = NWEndpoint.url(URL(string: "https://relay.example.com")!)
let relayServer = ProxyConfiguration.RelayHop(http3RelayEndpoint: relayEndpoint)
let relayConfig = ProxyConfiguration(relayHops: [relayServer])
var context = NWParameters.PrivacyContext(description: "my relay")
context.proxyConfigurations = [relayConfig]
let parameters = NWParameters.tls
parameters.setPrivacyContext(context)
let connection = NWConnection(host: "www.example.com", port: 443, using: parameters)
connection.start(queue: .main)
关键点:
NWParameters.PrivacyContext是 iOS 17 / macOS Sonoma 新增的类型,用来封装隐私配置proxyConfigurations接受一个ProxyConfiguration数组setPrivacyContext将隐私上下文绑定到连接参数上- 适用于需要底层网络控制的场景,比如自定义协议或非 HTTP 流量
方式二:URLSession(06:07)
import Network
let relayEndpoint = NWEndpoint.url(URL(string: "https://relay.example.com")!)
let relayServer = ProxyConfiguration.RelayHop(http3RelayEndpoint: relayEndpoint)
let relayConfig = ProxyConfiguration(relayHops: [relayServer])
let config = URLSessionConfiguration.default
config.proxyConfigurations = [relayConfig]
let mySession = URLSession(configuration: config)
let url = URL(string: "https://www.example.com/api/v1/employees")!
let (data, response) = try await mySession.data(from: url)
关键点:
URLSessionConfiguration新增了proxyConfigurations属性- 配置后,该
URLSession的所有请求都通过中继路由 - 使用 Swift concurrency 的
async/awaitAPI 获取数据 - 服务器只能看到中继的 IP 地址,无法获取用户的真实 IP
方式三:WebKit(06:30)
import Network
let relayEndpoint = NWEndpoint.url(URL(string: "https://relay.example.com")!)
let relayServer = ProxyConfiguration.RelayHop(http3RelayEndpoint: relayEndpoint)
let relayConfig = ProxyConfiguration(relayHops: [relayServer])
let webkitConfig = WKWebViewConfiguration()
webkitConfig.websiteDataStore = WKWebsiteDataStore.nonPersistent()
webkitConfig.websiteDataStore.proxyConfigurations = [relayConfig]
let webView = WKWebView(frame: .zero, configuration: webkitConfig)
let url = URL(string: "https://www.example.com/api/v1/employees")!
webView.load(URLRequest(url: url))
关键点:
WKWebsiteDataStore新增了proxyConfigurations属性- 使用了
nonPersistent()数据存储,这意味着不保留 cookie 和缓存,增强隐私 - WebView 中的所有网络请求都会通过中继
- 适用于内嵌浏览器场景,比如 OAuth 登录流程
企业场景:通过配置描述文件部署中继
企业不需要让员工手动配置。管理员可以通过 MDM(移动设备管理)下发配置描述文件(09:15):
<dict>
<key>PayloadType</key>
<string>com.apple.relay.managed</string>
<key>Relays</key>
<array>
<dict>
<key>HTTP3RelayURL</key>
<string>https://relay.example.com</string>
<key>PayloadCertificateUUID</key>
<string>5AB702EC-32F3-48A9-94FE-8EA1C67ACF46</string>
</dict>
</array>
<key>MatchDomains</key>
<array>
<string>internal.example.com</string>
</array>
</dict>
关键点:
PayloadType为com.apple.relay.managed,这是新增的配置描述文件类型HTTP3RelayURL指定中继服务器地址PayloadCertificateUUID引用同一描述文件中定义的证书,用于 TLS 验证MatchDomains限定中继只代理指定域名的流量,其他流量不受影响- 这是 VPN 的重大改进:只代理需要的流量,而不是全部
企业场景:通过 NetworkExtension 编程配置
如果企业有自己的管理 App,也可以通过 NetworkExtension 框架编程配置中继(09:42):
import NetworkExtension
let newRelay = NERelay()
let relayURL = URL(string: "https://relay.example.com:443/")
newRelay.http3RelayURL = relayURL
newRelay.http2RelayURL = relayURL
newRelay.additionalHTTPHeaderFields = ["Authorization" : "PrivateToken=123"]
let manager = NERelayManager.shared()
manager.relays = [newRelay]
manager.matchDomains = ["internal.example.com"]
manager.isEnabled = true
do {
try await manager.saveToPreferences()
} catch let saveError {
// Handle error
}
关键点:
NERelay是 NetworkExtension 框架新增的类,表示一个中继配置- 同时支持 HTTP/3 和 HTTP/2 协议,作为 fallback
additionalHTTPHeaderFields可以添加自定义 HTTP 头,用于身份验证(如Authorizationtoken)NERelayManager.shared()是单例,管理系统级的中继配置matchDomains限定中继的应用范围,只对指定域名生效saveToPreferences()将配置持久化到系统,需要用户授权
核心启发
-
为健康/金融类 App 添加网络隐私保护:如果你的 App 处理敏感数据(健康记录、财务信息、位置数据),通过
URLSessionConfiguration.proxyConfigurations配置中继,让服务器无法获取用户真实 IP。为什么值得做:隐私法规(GDPR、CCPA)对用户数据的收集和处理有严格要求,隐藏 IP 是降低合规风险的有效手段。怎么开始:选择一个支持 MASQUE 协议的中继服务商,在URLSessionConfiguration中设置proxyConfigurations,然后验证服务器日志中是否只看到中继 IP。 -
用中继替代企业 VPN 访问内部 API:企业 App 需要访问内网资源时,不再需要让员工安装 VPN 客户端。通过 MDM 下发
com.apple.relay.managed配置描述文件,中继只代理MatchDomains中指定的域名。为什么值得做:VPN 连接慢、掉线需要手动重连、干扰非企业流量——这些痛点中继都不存在。怎么开始:搭建支持 MASQUE 协议的中继服务器,创建配置描述文件,通过 MDM 推送到设备,然后通过NERelayManager或 URLSession 使用。 -
使用双中继(two-hop)架构实现更强的隐私保护:在
relayHops数组中配置两个RelayHop,第一个中继知道用户 IP 但看不到内容,第二个中继能看到内容但不知道用户 IP。为什么值得做:单中继方案中,中继提供商同时知道你是谁和你在访问什么。双中继架构确保没有任何单一实体能关联两者。怎么开始:选择两个不同的中继提供商,创建两个RelayHop对象,放入ProxyConfiguration(relayHops: [hop1, hop2])。 -
为 WebView 内容添加网络隔离:如果你的 App 内嵌了 WebView 展示第三方内容,通过
WKWebsiteDataStore.nonPersistent()配合proxyConfigurations,让 WebView 的网络请求通过中继路由且不保留本地数据。为什么值得做:内嵌浏览器是隐私泄露的重灾区——第三方脚本、追踪器都可以通过 WebView 获取用户信息。怎么开始:创建WKWebViewConfiguration,设置websiteDataStore为nonPersistent()并配置proxyConfigurations。 -
用自定义 HTTP 头实现无密码认证:通过
NERelay.additionalHTTPHeaderFields添加Authorization头,中继服务器可以验证请求来源而不需要用户输入密码。为什么值得做:企业中继访问内部资源时,传统 VPN 需要额外的用户名密码认证步骤。自定义 HTTP 头可以与 SSO 系统集成,实现无感知认证。怎么开始:在企业 IdP 中生成 API token,通过additionalHTTPHeaderFields传入,中继服务器端验证 token。
关联 Session
- Reduce network delays with L4S — L4S 是一种降低网络延迟的新技术,与中继配合可以同时改善隐私和性能
- Build robust and resumable file transfers — URLSession 的大文件传输能力,中继配置可以直接应用到这些传输上
- What’s new in managing Apple devices — 设备管理的新能力,包括中继配置描述文件的 MDM 下发
- Explore advances in declarative device management — 声明式设备管理的更新,企业部署中继的基础设施
评论
GitHub Issues · utterances