探索现代的移动网络
作者:盒子里的薛定谔(Darren Chen),iOS 工程师,目前就职于百度 App 移动技术平台组
Sessions: https://developer.apple.com/videos/play/wwdc2020/10111/
https://developer.apple.com/videos/play/wwdc2020/10047/
https://developer.apple.com/video你可以为系统级 DNS 设置根据不同的接入网设置不同的规则。s/play/wwdc2020/10110/
https://developer.apple.com/videos/play/wwdc2020/10113/
IPv6
IP 协议第 6 版(英语:Internet Protocol version 6,缩写:IPv6)是 IP 协议的最新版本,用作互联网的协议。用它来取代 IPv4 主要是为了解决 IPv4 地址枯竭问题,同时它也在其他方面对于 IPv4 有许多改进。
IPv6 的设计目的是取代 IPv4,然而长期以来 IPv4 在互联网流量中仍占据主要地位,IPv6 的使用增长缓慢。在 2019 年 12 月,通过 IPv6 使用 Google 服务的用户比例首次超过 30%。
背景与目标
现在的互联网络发展蓬勃,截至 2018 年 1 月,全球互联网用户已达 40.21 亿,IPv4 仅能提供约 42.9 亿个IP地址。虽然目前的 NAT (网络地址转换)及 CIDR (无类别域间路由)等技术可延缓 IP 地址匮乏的现象,但为求从根本解决问题,从 1990 年开始,IETF 开始规划 IPv4 的下一代协议,除了要解决即将遇到的 IP 地址短缺问题外,还要发展更多的扩展,为此 IETF 小组创建 IPng,以让后续工作顺利进行。1994 年,各 IPng 领域的代表们于多伦多举办的 IETF 会议中,正式提议 IPv6 发展计划,该提议直到同年的 11 月 17 日才被认可,并于 1996 年 8 月 10 日成为 IETF 的草案标准,最终 IPv6 在 1998 年 12 月由互联网工程工作小组以互联网标准规范(RFC 2460[1])的方式正式公布。
IPv6 的计划是创建未来互联网扩展的基础,其目标是取代 IPv4,虽然 IPv6 在 1994 年就已被 IETF 指定作为 IPv4 的下一代标准,由于早期的路由器、防火墙、企业的ERP及相关应用程序都需要改写,所以在世界范围内使用 IPv6 部署的网站与 IPv4 相比还非常的少,技术上仍以双栈并存居多。预计在 2025 年以前 IPv4 仍会被支持,以便给新协议的修正留下足够的时间。
与 IPv4 的比较
在因特网上,数据以数据包的形式传输。IPv6 定义了一种新的数据包格式,目的是为了最小化路由器处理的报头。由于 IPv4 和 IPv6 的报头有很大不同,因此这两种协议无法互操作。但是在大多数情况下,IPv6 仅仅是对 IPv4 的一种保守扩展。除了嵌入了互联网地址的那些应用协议(如 FTP 和 NTP,新地址格式可能会与当前协议的语法冲突)以外,大多数传输层和应用层协议几乎不怎么需要修改就可以在 IPv6 上运行。IPv4 和 IPv6 的具体比较,可参考https://www.ibm.com/support/knowledgecenter/zh/ssw_ibm_i_72/rzai2/rzai2compipv4ipv6.htm[2]。
更大的地址空间
相比 IPv4,IPv6 主要的优势是更大的地址空间, IPv6地址的大小为128位,而IPv4地址为32位。在 IPv6 下,有 = 340,282,366,920,938,463,463,374,607,431,768,211,456 (约 ) 个地址空间,可以做到“让地球上的每一粒沙子都具有独立的IP地址”。其中的一些地址作为保留地址留给特殊场合。
尽管 IPv6 的地址空间很大,但 IPv6 的设计者并不是要确保可用地址的饱和度。而是,较长的地址简化了地址分配,实现了高效的路由聚合,并允许实现特殊的寻址功能。在 IPv4 中,开发了复杂的无类别域间路由(CIDR)方法,以充分利用小地址空间。IPv6 中子网的标准大小是 个地址,是整个 IPv4 地址空间大小的平方,大约是 40 亿倍。因此,IPv6 中实际的地址空间利用率将很小,但是子网空间大和分层路由聚合可提高网络管理和路由效率。
多播
多播是在单次发送操作中将数据包传输到多个目的地的过程,是 IPv6 基本规范的一部分。在 IPv4 中,这是可选的功能。IPv6 多播寻址具有与 IPv4 多播相同的功能和协议,但通过消除对某些协议的需求,也提供了更改和改进。IPv6 不实现传统的 IP 广播,即使用特殊的广播地址将数据包传输到连接的链路上的所有主机,因此没有定义广播地址。在 IPv6 中,通过向地址为 ff02 :: 1 的本地链路所有节点多播组发送一个数据包,可以实现相同的结果,这类似于对地址 224.0.0.1 的 IPv4 多播。IPv6 还提供了新的多播实现,包括将集合点地址嵌入 IPv6 多播组地址中,从而简化了域间解决方案的部署。
在IPv4中,组织机构甚至很难获得一个全球可路由的多播组分配,并且域间解决方案的实现是很晦涩的。本地 Internet 注册表为 IPv6 分配的单播地址至少具有 64 位路由前缀, 产生 IPv6 中可用的最小子网大小(也是 64 位)。通过这样的分配,可以将单播地址前缀嵌入 IPv6 多播地址格式,同时仍提供 32 位块,地址的最低有效位或大约 42 亿个多播组标识符。因此,IPv6 子网的每个用户都会自动为多播应用程序提供一组全局可路由的特定源的多播组。
无状态地址自动配置(SLAAC)
IPv6主机可以自动分配。每个接口都有一个自己生成的链路本地地址,当连接到网络时,将执行冲突解决,并且路由器通过路由广播提供网络前缀。路由器的无状态配置可以通过特殊的路由器重编号协议来实现。必要时,主机可以通过 DHCPv6 或使用静态方法手动配置。
与 IPv4 一样,IPv6 支持全局唯一的 IP 地址。IPv6 的设计旨在重新强调网络设计的端到端原则,该原则最初是在早期 Internet 的建立过程中通过使网络地址转换过期而构想的。因此,网络上的每个设备都可以直接从任何其他设备全局寻址。
稳定、唯一、可全局寻址的 IP 地址将有助于在整个网络中跟踪设备。因此,这样的地址有着移动设备(例如笔记本电脑和手机)特别关注的隐私问题。为了解决这些隐私问题,SLAAC 协议包括通常称为“隐私地址”或更准确地说是“临时地址”的内容, RFC 4941[3],“ IPv6 中无状态地址自动配置的隐私扩展”。临时地址是随机且不稳定的。典型的消费者设备每天会生成一个新的临时地址,一个星期后将忽略发送到旧地址的流量。Windows 默认从 XP SP1 开始使用临时地址,OS X 从 10.7 开始使用,Android 从 4.0 开始使用,iOS 从 4.3 版本开始使用。Linux发行版对临时地址的使用则有所不同。
IPv4 的一个主要工作是为具有不同路由前缀的新连接提供商重新编号现有网络。但是,使用IPv6,更改几个路由器广播的前缀原则上可以对整个网络重新编号,因为主机标识符(地址的最低有效64位)可以由主机独立进行自我配置。
SLAAC地址生成方法取决于实现。IETF 建议地址是确定性的,但语义上不透明。
IPsec
IPsec 最初是为 IPv6 开发的,但首先在 IPv4 中建立了广泛的部署,然后对其进行了重新设计。IPsec 是所有 IPv6 协议实现中的强制性部分,并且建议使用 Internet 密钥交换(IKE),但是在RFC 6434[4]中,由于将 IPsec 包含在 IPv6 实现中而被降级为建议,因为认为不要求完全实现 IPsec 实现适用于可能使用 IPv6 的所有类型的设备。但是,从实现 IPsec 的 RFC 4301[5] IPv6 协议开始,需要实现 IKEv2,并且需要支持最少的一组加密算法。此要求将有助于使 IPsec 实现在不同供应商的设备之间具有更高的互操作性。IPsec 验证头(AH)和封装安全有效载荷头(ESP)被实现为 IPv6 扩展头。
路由器简化过程
IPv6中的数据包头比IPv4头简单。许多很少使用的字段已移至可选的扩展头。使用简化的 IPv6 数据包报头,可以简化路由器的数据包转发过程。尽管 IPv6 数据包报头的大小至少是 IPv4 数据包报头的两倍,但是在某些情况下,路由器处理仅包含基本 IPv6 数据包的数据包可能会更高效,因为由于报头对齐,路由器需要的处理较少以匹配常见字词大小。但是,许多设备在软件(而不是硬件)中实现了对 IPv6 的支持,因此导致非常差的数据包处理性能。另外,对于许多实施方式,扩展头的使用会导致数据包由路由器的 CPU 处理,从而导致性能下降甚至安全问题。
此外,IPv6标头不包含校验码。IPv4 头校验码是针对 IPv4 头计算的,并且每次生存时间(在IPv6协议中称为跳数限制)减少1时,都必须由路由器重新计算。IPv6 标头中不存在校验码,这加深了 Internet 设计的端到端原则,该原则设想网络中的大多数处理都在叶节点中进行。假定通过数据链路层或更高层协议(即传输层的传输控制协议(TCP)和用户数据报协议(UDP))中的错误检测来确保对封装在 IPv6 数据包中的数据进行完整性保护。因此,虽然 IPv4 允许 UDP 数据报标头不具有校验码(报头字段中用 0 表示),但 IPv6 要求 UDP 标头中具有校验码。
IPv6 路由器不执行IP分段。IPv6 主机需要执行路径 MTU 发现,执行端到端分段或发送不大于默认最大传输单位(MTU)(即1280个八位字节)的数据包。
移动性
与移动 IPv4 不同,移动 IPv6 避免了三角路由,因此与本地 IPv6 一样高效。IPv6 路由器还可以允许整个子网迁移至新的路由器连接点,而无需重新编号。
扩展头
IPv6 数据包头的最小大小为 40 个八位组(320位)。作为扩展,增加了一些选项。这提供了将来扩展协议而又不影响核心分组结构的机会。但是,RFC 7872[6] 指出,某些网络运营商在穿越独立系统时会丢弃带有扩展头的 IPv6 数据包。
Jumbograms
IPv4 将数据包限制为有效负载 65535()个八位组。IPv6节点可以处理超过此限制的数据包(称为 Jumbograms),该数据包可能多达 4,294,967,295()个八位组。Jumbograms 的使用可以提高 MTU 连接的性能。Jumbo Payload Option 扩展头指示了如何使用 Jumbograms。
优势
使用 IPv6 进行连接有更低的延迟,并且通常比 IPv4 性能强。部分原因是因为更少的 NAT,并且网络设备更现代。
苹果与 IPv6
从 2016 年开始,苹果就已要求提交到 App Store 的应用必须支持通过 Mac 共享的具有 NAT64 支持的 IPv6-only 网络。
现状
最近一个月全世界的苹果设备所创建的连接中,IPv6 的连接占比为 26%。其中使用 IPv4 的 74% 占比中仍有 20% 的连接本可以使用 IPv6,而服务器并未启用。由于减少了 NAT 的使用和提升了路由效率,IPv6 建连时间的中位值比 IPv4 要快 1.4 倍。
如何改造
如果客户端使用的是 URLSession
或 Network.framework
的现代 API,那么只需要服务端提供了 IPv6 的支持即可享受 IPv6 为用户体验带来的提升了。
HTTP/2
HTTP/2(超文本传输协议第2版,最初命名为HTTP 2.0),简称为 h2(基于 TLS/1.2 或以上版本的加密连接)或 h2c(非加密连接),是 HTTP 协议的的第二个主要版本,使用于万维网。
HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新,主要基于 SPDY 协议。它由 IETF 的 httpbis 工作小组进行开发,该组织于 2014 年 12 月将 HTTP/2 标准提议递交至 IESG 进行讨论,于 2015 年 2 月 17 日被批准。
HTTP/2 标准于 2015 年 5 月以 RFC 7540[7] 正式发表。HTTP/2 的标准化工作由 Chrome、Opera、Firefox、Internet Explorer 11、Safari、Amazon Silk 及 Edge 等浏览器提供支持。
多数主流浏览器已经在 2015 年底支持了该协议。此外,根据 W3Techs 的数据,截至 2020 年 8 月,全球有 36.5% 的网站支持了 HTTP/2。
目标
制定 HTTP/2 协议时,工作组章程关注了下列目标和关心的问题:
创建一个协商协议标准,即应用层协议协商(ALPN),以便客户端能够从 HTTP/1.0、HTTP/1.1、HTTP/2 乃至其他非 HTTP 协议中做出选择;
与 HTTP/1.1 保持高度兼容性(比如请求方法、状态码、URI 和大部分 HTTP 头);
减少网络延迟,提高浏览器页面加载速率;
支持现有的 HTTP 应用场景,包括桌面和移动设备浏览器,网络 API,不同规格的网络服务器和正向代理、反向代理服务器软件、防火墙、CDN 等。
新特性
二进制分帧
HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。接下来我们介绍几个重要的概念:
流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N);
消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成;
帧:HTTP 2.0 通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流,承载着特定类型的数据,如 HTTP 首部、负荷,等等
HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。
多路复用
在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。在 HTTP/2 中,有了二进制分帧之后,HTTP/2 不再依赖 TCP 连接去实现多流并行了,在 HTTP/2 中:
同域名下所有通信都在单个连接上完成。
单个连接可以承载任意数量的双向数据流。
数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
这一特性,使性能有了极大提升:
同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,消除了因多个 TCP 连接而带来的延时和内存消耗。
并行交错地发送多个请求,请求之间互不影响。
并行交错地发送多个响应,响应之间互不干扰。
在 HTTP/2 中,每个请求都可以带一个 31bit 的优先值,0 表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。
首部压缩( HPACK 算法)
在 HTTP/1 中,我们使用文本的形式传输 header,在 header 携带 cookie 的情况下,可能每次都需要重复传输几百到几千的字节。
为了减少这块的资源消耗并提升性能, HTTP/2 对这些首部采取了压缩策略:
HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
首部表在 HTTP/2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值
例如下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销
Server Push
Server Push 即服务端能通过 push 的方式将客户端需要的内容预先推送过去,也叫“cache push”。可以想象以下情况,某些资源客户端是一定会请求的,这时就可以采取服务端 push 的技术,提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间。当然在浏览器兼容的情况下你也可以使用 prefetch。例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 时再发送这些请求。
服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略,换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。
协议的比较
HTTP/2 与 HTTP/1.1
HTTP/2 相比 HTTP/1.1 的修改并不会破坏现有程序的工作,但是新的程序可以借由新特性得到更好的速度。
HTTP/2 保留了 HTTP/1.1 的大部分语义,例如请求方法、状态码乃至 URI 和绝大多数 HTTP 头部字段一致。而 HTTP/2 采用了新的方法来编码、传输 客户端<——>服务器 间的数据。
高效的网站通过减小图像和脚本等资源(减少代码量并将较小的代码打包成包,而不影响其功能)来最小化呈现整个页面所需的请求数。但是,缩小不一定方便也不高效,可能仍然需要单独的 HTTP 连接来获取页面和缩小的资源。HTTP/2 允许服务端“推送”内容,来响应客户端更多的请求。这允许服务器提供 web 浏览器将需要呈现 web 页面的已知数据,而无需等待浏览器检查第一个响应,也无需额外的请求周期开销。
HTTP/2 初稿(SPDY 的副本)中的其他性能改进来自于请求和响应的多路复用,以避免 HTTP 1 中的某些队头阻塞问题(即使使用了 HTTP 管道)、报头压缩以及请求优先级。但是, 由于HTTP/2 运行在单个 TCP 连接之上,因此如果 TCP 数据包丢失或延迟,则仍有可能发生队头阻塞传输。HTTP/2 不再支持 HTTP/1.1 的分块传输编码机制,因为它为数据流提供了自己更高效的机制。
HTTP/1.1 与 SPDY
SPDY 是一个由 Google 主导的研究项目发明的 HTTP 替代协议。SPDY一开始主要关注降低延迟,采用了 TCP 通道,但是使用了不同的协议来达到此目的。其与 HTTP/1.1 相比,主要的改变有:
实现无需先入先出的多路复用
为简化客户端和服务器开发的消息帧机制
强制性压缩(包括 HTTP 头)
优先级排序
双向通讯
HTTP/2 与 SPDY
HTTP/2 的开发基于 SPDY 进行跃进式改进。在诸多修改中,最显著的改进在于,HTTP/2使用了一份经过定制的压缩算法,基于 Hoffman 编码,以此替代了 SPDY 的动态流压缩算法,以避免对协议的 Oracle 攻击——这一类攻击以 CRIME 为代表。此外,HTTP/2 禁用了诸多加密包,以保证基于TLS的连接的前向安全。
苹果与 HTTP/2
URLSession 和 Network.framework 中已经内置了对 HTTP/2 的支持。
现状
在过去的一个月中,Safari 的 HTTP 请求中,有 79% 的请求使用了 HTTP/2, 使用 HTTP/2 的 URLSessionTask 的请求的耗时的中位值比使用 HTTP/1.1 的请求要高 1.8 倍。
如何改造
在服务端支持 HTTP/2 的情况下,如果客户端使用 URLSession
,则会默认协商为 HTTP/2 的连接。
TLS 1.3
传输层安全性协议(英语:Transport Layer Security,缩写:TLS)及其前身安全套接层(英语:Secure Sockets Layer,缩写:SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。网景公司(Netscape)在 1994 年推出首版网页浏览器-网景导航者时,推出 HTTPS 协议,以 SSL 进行加密,这是 SSL的起源。IETF 将 SSL 进行标准化,1999 年公布 TLS 1.0 标准文件(RFC 2246[8])。随后又公布 TLS 1.1 (RFC 4346[9],2006年)、TLS 1.2(RFC 5246[10],2008年)和 TLS 1.3(RFC 8446[11],2018年)。在浏览器、电子邮件、即时通信、VoIP、网络传真等应用程序中,广泛使用这个协议。许多网站,如Google、Facebook、Wikipedia等也以这个协议来创建安全连接、发送数据。目前已成为互联网上保密通信的工业标准。
握手框架
TLS 1.2 完整握手框架:
TLS 1.3 完整握手框架:
第1步,客户端发送 ClientHello 消息,该消息主要包括客户端支持的协议版本、DH 密钥交换参数列表 KeyShare;
第2步,服务端回复 ServerHello,包含选定的加密套件;发送证书给客户端;使用证书对应的私钥对握手消息签名,将结果发送给客户端;选用客户端提供的参数生成 ECDH 临时公钥,结合选定的 DH 参数计算出用于加密 HTTP 消息的共享密钥;服务端生成的临时公钥通过 KeyShare 消息发送给客户端;
第3步,客户端接收到 KeyShare 消息后,使用证书公钥进行签名验证,获取服务器端的 ECDH 临时公钥,生成会话所需要的共享密钥;
第4步,双方使用生成的共享密钥对消息加密传输,保证消息安全。
TLS1.3的握手流程与TLS1.2最大的区别,就在于TLS1.3提前走了加密,TLS1.2需要在双方明文交换了key exchange信息之后才会走加密通道,而TLS1.3在sever端发送完ServerHello信息之后就会走加密通道,就连证书信息也是加了密的。TLS1.3极大的缩短了建立连接所需要的时间。
TLS 1.3 改进增加了“不额外增加网络延时”模式(0-RTT)。对于近期访问过的站点,可以直接发送有用的数据,而不需要经过握手。
TLS 1.2 中通过 1 个 RTT 即可完成会话恢复,TLS 1.3 在恢复会话时只需要 0-RTT。
与 TLS 1.2 比较
TLS 1.3在 RFC 8446 中定义,于2018年8月发表。它与TLS 1.2的主要区别包括:
将密钥交换算法(如ECDHE)和认证算法(如RSA)从密码包中分离出来。
移除对弱且较少使用的椭圆曲线类型的支持。
移除 MD5、SHA1 密码散列函数的支持。
即使使用之前的配置依然请求数字签名。
融合 HKDF 与半临时 DH 提案。
替换使用 PSK 和 ticket 的恢复。
支持 1-RTT 握手并初步支持 0-RTT。
通过在 (EC)DH 密钥协商期间使用临时密钥来保证完善的前向安全性。
放弃对许多不安全或过时特性的支持,包括数据压缩、重新协商、非 AEAD 加密算法、静态 RSA 和静态 DH 密钥交换、自定义 DHE 分组、EC 点格式协商、更改密码规范的协议、UNIX 时间的 Hello 消息,以及输入到 AEAD 密码的长度字段 AD。
禁止SSL或RC4协商以实现向后兼容性。
较 TLS 1.2 速度更快,性能更好。
移除 RC4 加密算法的支持。
集成会话 hash 的使用。
弃用记录层版本号并冻结该号以提高向后兼容性。
将一些安全相关的算法细节从附录移动到标准,并将 ClientKeyShare 降级到附录。
支持 Ed25519 和 Ed448 数字签名算法。
支持 X25519 和 X448 密钥交换协议。
支持带 Poly1305 消息验证码的 ChaCha20 加密算法。
增加了对发送多个OCSP响应的支持。
支持加密服务器名称指示(Encrypted Server Name Indication, ESNI)。
在ServerHello 之后加密所有握手消息。
进展
网络安全服务(NSS)是由 Mozilla 开发并由其网络浏览器 Firefox 使用的加密库,自 2017 年 2 月起便默认启用 TLS 1.3。随后 TLS 1.3 被添加到 2017 年 3 月发布的 Firefox 52.0 中,但它由于某些用户的兼容性问题,默认情况下禁用,直到 Firefox 60.0 才正式默认启用。
Google Chrome 曾在 2017 年短时间将 TLS 1.3 设为默认,然而由于类似 Blue Coat Systems 等不兼容组件而被取消。
wolfSSL 在 2017 年 5 月发布的 3.11.1 版本中启用了 TLS 1.3。作为第一款支持 TLS 1.3 的的商业实现,wolfSSL 3.11.1 支持 TLS 1.3 Draft 18(现已支持到 Draft 28),同时官方也发布了一系列关于 TLS 1.2 和 TLS 1.3 性能差距的博客。
在 2018 年 9月,广受欢迎的 OpenSSL 项目发布了 1.1.1 版本,其中对 TLS1.3 的支持为“重磅新特性”。
苹果与 TLS 1.3
在 iOS 12 和 macOS Mojave 中,我们提供了 TLS1.3 的预览,您可以在其中启用 TLS 1.3 标准的初始版本,并针对服务器部署对其进行测试。现在,该标准已经完成,并且自 iOS 13.4 起 URLSession 和 Network.framework 默认启用 TLS 1.3。
现状
在过去的一个月中,我们已经看到大约 49% 的连接在运行最新 iOS 的设备上使用 TLS 1.3。建立使用 TLS 1.3 的连接的速度比使用 TLS 1.2 的连接快 1.3 倍。
如何改造
如果在服务器上启用了 TLS 1.3,iOS 的现代网络 API (URLSession 和 Network.framework)将默认协商 TLS 1.3。
Multipath TCP
Multipath TCP(MPTCP)是 IETF 的 Multipath TCP 工作组的一项持续努力,该工作组旨在允许 TCP 连接使用多条路径来最大程度地利用和扩大资源。
2012年10月,MPTCP 代理版本 0.9 源代码,由阿尔卡特朗讯公司于发布。
2013 年 1 月,IETF 在 RFC 6824[12] 中发布了 Multipath 规范作为实验标准。2020 年 3 月,Multipath 的版本 1 已获得批准并更新为 RFC 8684[13]。
MPTCP 主要分三部分:
master subsocket
Multi-path control block(mpcb)
slave subsocket
master subsocket 是一个标准的 socket 结构体用于 TCP 通信。mpcb 提供开启或关闭子通道、选择发送数据的子通道以及重组报文段的功能。slave subsocket 对应用程序并不可见,他们都是被 mpcb 管理并用于发送数据。
通俗理解:
MultiPath TCP 允许在一条 TCP 链路中建立多个子通道。当一条通道按照三次握手的方式建立起来后,可以按照三次握手的方式建立其他的子通道,这些通道以三次握手建立连接和四次握手解除连接。这些通道都会绑定于 MPTCP session,发送端的数据可以选择其中一条通道进行传输。
好处
MPTCP 实现了资源的反向多路复用,这样将 TCP 吞吐量提高到所有可用的链路级通道的总和,而不是像普通 TCP 那样使用单个通道。MPTCP 与单 TCP 向后兼容。
MPTCP 在无线网络中特别有用, 同时使用 Wi-Fi 和移动网络是典型的用例。除了通过反向多路复用提高吞吐量外,还可以在用户移入或移出覆盖范围时添加或删除链接,而不会中断端到端 TCP 连接。
因此,通过在传输层中进行抽象解决了链路切换的问题,而无需在网络或链路级别上使用任何特殊的机制。然后,根据 Internet 的端到端原则,可以在端上实现切换而无需子网中的特殊功能。
MPTCP 还为数据中心环境带来了性能优势。与使用 802.3ad 链路聚合的以太网通道绑定相反,MPTCP 可以在多个接口之间平衡单个 TCP 连接并达到很高的吞吐量。
MPTCP 导致许多新问题。从网络安全角度来看,多路径路由会导致跨路径数据碎片化,从而导致防火墙和恶意软件扫描程序仅看到单个路径的流量时效率低下。此外,通过端到端加密协议,SSL 解密将变得效率低下。
苹果与 MPTCP
2013 年 9月 18 日发布的苹果 iOS 7 是 MPTCP的第一个大规模商业部署,从 iOS 7 开始, iOS 系统上的任意应用程序都可以使用 MPTCP;
2014 年 10 月 16日发布的苹果 Mac OS X 10.10 开始支持 MPTCP;
2017年9月,苹果在 WWDC2017 上宣布,iOS 11 开始针对 MPTCP 提供接口支持;
2019年 WWDC 苹果宣布除了 Siri,Apple Music 也支持 MPTCP。
如何改造
在 Xcode 中添加 MultiPath 的 entitlement 后,在代码中可以通过设置 URLSession 的 configuration 或者 Network.framework 的 NWParameters 对象来支持 Multipath 服务类型属性。
/* MultipathServiceType
.none 不启用 MPTCP
.handover 只有当主链路不能用的时候,才会使用第二条链路,这种模式可靠性高
.interactive 当主链路不够用的时候,比如丢包、延时很长等情况,就会启用第二条链路,这种模式延时低
.aggregate 为了更大的带宽,多条链路可以一起使用,当前仅限开发测试使用
*/
// URLSession
let config = URLSessionConfiguration.default
config.multipathServiceType = .handover
let session = URLSession(configuration: config)
let stringUrl = "http://xxx.xxx"
let url = URL(string: stringUrl)
let task = session.dataTask(with: url!, completionHandler: { (data, resp, err) in
// 请求结果处理
})
task.resume()
// Network.framework
let parameters = NWParameters.tls
parameters.multipathServiceType = .handover
let connection = NWConnection(to: endpoint, using: parameters)
let listener = try NWListener(using: parameters)
在服务端,还需要做一些额外的部署工作,可参考 https://multipath-tcp.org [14]。
现状
Apple Music 应用从去年开始开始使用 MPTCP 后,卡顿率减少了 13%,卡顿发生时,卡顿时长减少了 22%。
加密 DNS
背景
DNS 是互联网上个人活动最重要的数据泄漏之一。
简单来说,原因如下:
Internet 上几乎所有活动都是以 DNS 查询(通常是多个查询)开始的。DNS 的一项关键功能是将人类可读的域名(例如 example.com)映射到计算机相互连接所需的 IP 地址。
这些查询不仅可以揭示个人访问了哪些网站,还可以揭示有关其他服务(例如电子邮件联系人或聊天服务的域)的元数据。
虽然 DNS 中的数据是公开的,但最终用户进行的单个查询不应公开。
但是,DNS 查询以明文形式发送(使用 UDP 或 TCP),这意味着被动的窃听者可以观察到所有执行的 DNS 查找。
DNS 是一个跨越全球范围的全球分布式系统,通常在许多不同国家/地区使用服务器。
众所周知,NSA 使用 MORECOWBELL 和 QUANTUMDNS 工具执行 DNS 流量的秘密监视,大规模监视和劫持。
一些 ISP 在解析器上记录 DNS 查询,并以最终用户不知道或不知道的方式与第三方共享此信息。
某些 ISP 将用户信息(例如,用户 ID 或 MAC 地址)嵌入到 ISP 解析器的 DNS 查询中,以提供诸如父母过滤之类的服务。
一些 CDN 将用户信息(客户端子网)嵌入到从解析程序到权威服务器的查询中(以对最终用户进行地理定位)。这允许查询与特定子网的关联。
请注意,即使使用 VPN,某些 VPN 仍会通过将未加密的 DNS 查询发送给 ISP 来泄漏您的 DNS 查询。可以使用来自anonymyster.com[15] 的工具来检查您的 VPN 是否正在发生这种情况。
解决方案
DNS-over-TLS (DoT)
DoT 方案在 2016 年发表于 RFC7858[16] 和 RFC8310[17],使用 853 端口。主要思想是 Client 和 Server 通过 TCP 协议建立 TLS 会话后再进行 DNS 传输,Client 通过 SSL 证书验证服务器身份。
DNS-over-HTTPS(DoH)
DoH 方案发表于 RFC8484[18],使用 https://dns.example.com/dns-query{?dns} 来查询服务器的 IP,复用 https 的 443 端口,流量特征比较小。DoH 会对 DNS 服务器进行加密认证,不提供 fallback 选项。目前 Cloudflare、Google 等服务商对 DoH 提供了支持。
DNS-over-DTLS
DNS-over-DTLS 和 DoT 类似,区别在于使用 UDP 协议而不是 TCP 协议。RFC8094[19] 在 2017 年 2 月将 DNS-over-DTLS 指定为实验标准。据我们所知,尚无计划或正在进行的DNS-over-DTLS 实现。
DNS-over-QUIC
DNS-over-QUIC 安全特性和 DoT 类似,但是性能更高,目前没有合适的软件实现。草案已于 2017 年 4 月提交给 IETF QUIC DNS-over-QUIC 工作组
DNSCrypt
DNSCrypt 使用 X25519-XSalsa20Poly1305 而非标准的 TLS,且 DNSCrypt 的 Client 需要额外的软件,Server 需要的专门的证书。
DNSCurve
DNSCurve[20] 于 2010 年开发,旨在将解析器加密为权威通讯。它不是由 IETF 标准化的。
HTTPDNS
早在2014年,国内就已经提出了 HTTPDNS 的概念,并且逐步在移动客户端 APP 中应用起来,目前为国内厂商解决 DNS 劫持问题的通用方案之一。
HTTPDNS 使用 HTTP 协议或者加密的 HTTPS 协议进行域名解析,代替传统的基于 UDP 的 DNS 协议。这个定义看起来和 DoH 差不多,那他们有什么不同之处呢?
首先是标准不统一:HTTPDNS 诞生于DoH 协议之前,尚未形成统一标准,不同服务商报文封装都有自己的格式,移动客户端 APP 使用的时候通过集成服务商提供的 SDK 来接入 HTTPDNS 服务。
其次是应用场景不同:DoH 解决的是客户端到递归服务器这一段的数据加密问题,而 HTTPDNS 可以是加密的,可以是不加密,诞生的最初目的,其实还不是为了加密传输,而是要让客户端绕过运营商递归服务器,直接向域名权威服务器发起 DNS 查询请求,从而彻底解决运营商的递归的劫持、TTL 生效慢等问题。
DoH 和 HTTPDNS 从实现方式上来说并没本质的不同,都是将 DNS 解析协议封装到HTTP(S) 协议中,特别是 RFC8484 标准发布以后,如果 HTTPDNS 也遵循该标准,更是殊途同归,最终会融入 DoH 协议。
如果 DNS 解析过程未来都使用加密传输协议,那么现在一些常见的 DNS 安全问题将不复存在,比如域名劫持、TTL 时间被篡改、缓存污染等问题,企业业务运行将更加安全稳定。
苹果与加密 DNS
iOS 14 和 MacOS Big Sur 中还新增了对安全域名解析的程序的 HTTP/3 支持的实验性预览,可以在开发者选项中启用它。支持,包括 DNS-over-TLS 和 DNS-over-HTTPS。该支持不再提供给每个应用程序或浏览器单独的解析服务,而是提供给系统解析器,因此,一旦您配置了安全DNS,设备上的所有应用程序都将从中受益。
如何改造
系统级接管
我们可以使用 NEDNSSettingsManager API 编写一个 Network Extension 为加密 DNS 提供系统级的设置。
你可以为系统级 DNS 设置根据不同的接入网设置不同的规则。
DNS Settings Network Extension 安装后,需要前往设置->通用->VPN&Network->DNS 开启。
应用内接管
你可以通过配置 Network.framework 的 PrivacyContext 来对整个 App 开启加密 DNS:
你可以通过验证 DNS 协议类型来确认是否使用了加密 DNS:
通过 PrivacyContext 配置的加密 DNS 除了用于 Network.framework,也可以用于 URLSession 或更底层的 getaddrinfo:
HTTP/3
HTTP/3 是继 HTTP/2 之后即将用于在万维网上交换信息的 HTTP 协议的第三个主要版本。
虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。
HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1.1 了。
因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反倒只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
那么可能就会有人考虑到去修改 TCP 协议,其实这已经是一件不可能完成的任务了。因为 TCP 存在的时间实在太长,已经充斥在各种设备中,并且这个协议是由操作系统实现的,更新起来不大现实。
基于这个原因,Google 就更起炉灶搞了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上,HTTP/3 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。
HTTP/3 使用与 HTTP/1.1 和 HTTP/2 相同的语义(相同的操作,例如 GET 和 POST)和相同的响应代码(例如 200 或 404),但是使用不同的传输协议(QUIC),QUIC 可以识别这些语义并能够从数据包中恢复丢包,而使性能损失最小化。HTTP/3 的开发旨在解决因依赖 TCP 而引起的 HTTP/2 的主要问题(队头阻塞):因为HTTP/2 的多路复用对于 TCP 的丢失恢复机制不可见,因此丢失或重新排序的数据包会导致所有活动事务都陷入阻塞,无论该事务是否受到丢失数据包的影响。
截至 2020 年 8 月,HTTP/3 协议已成为 Internet 草案并具有多种实现方式,根据 W3Techs 的统计,前 1000 万个网站中有 6.7% 支持 HTTP/3。Firefox 和 Chrome 的稳定版本以当前形式支持 HTTP/3,但默认情况下将其禁用。Safari 14 将默认启用 HTTP/3。
QUIC
0-RTT
通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。那什么是 0RTT 建连呢?
这里面有两层含义:
传输层 0RTT 就能建立连接。
加密层 0RTT 就能建立加密连接。
上图左边是 HTTPS 的一次完全握手的建连过程,需要 3 个 RTT。就算是会话复用也需要至少 2 个 RTT。
而 QUIC 呢?由于建立在 UDP 的基础上,同时又实现了 0RTT 的安全握手,所以在大部分情况下,只需要 0 个 RTT 就能实现数据发送,在实现前向加密的基础上,并且 0RTT 的成功率相比 TLS 的会话记录单要高很多。
多路复用
虽然 HTTP/2 支持了多路复用,但是 TCP 协议终究是没有这个功能的。QUIC 原生就实现了这个功能,并且传输的单个数据流可以保证有序交付且不会影响其他的数据流,这样的技术就解决了之前 TCP 存在的问题。
同 HTTP2.0 一样,同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求,但是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖。比如下图中 stream2 丢了一个 UDP 包,不会影响后面跟着 stream3 和 stream4,不存在 TCP 队头阻塞。虽然 stream2 的那个包需要重新传,但是 stream3、stream4 的包无需等待,就可以发给用户。
另外 QUIC 在移动端的表现也会比 TCP 好。因为 TCP 是基于 IP 和端口去识别连接的,这种方式在多变的移动端网络环境下是很脆弱的。但是 QUIC 是通过 ID 的方式去识别一个连接,不管你网络环境如何变化,只要 ID 不变,就能迅速重连上。
加密认证的报文
TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改,注入和窃听。比如修改序列号、滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。但是 QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。这样只要对 QUIC 报文任何修改,接收端都能够及时发现,有效地降低了安全风险。
如上图所示,红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,全部经过加密。
前向纠错
QUIC 协议有一个非常独特的特性,称为向前纠错 (Forward Error Correction,FEC),每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失、请求重传、等待新数据包等步骤的时间消耗) 假如说这次我要发送三个包,那么协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。
优势
HTTP/3 具有内置的 TLS 1.3 的安全性,并提供与 HTTP / 2 相同的所有多路复用流支持。但是随着队头阻塞的进一步减少,因此任何单个请求或响应的丢失都不会阻塞其他可能不相关的消息。使用 QUIC 的 HTTP/3 还具有较高的保真度信息,以提供改进的拥塞控制和前向纠错。它还具有内置的移动性支持(连接迁移),因此网络过渡不会导致正在进行的操作失败,可以无缝地在新网络上继续运行而不会受到干扰。
苹果与 HTTP/3
HTTP/3 仍是 IETF 的一项正在进行中的规范,在该规范中,苹果将继续积极协作以帮助使 HTTP/3做好在全球范围内部署的准备。作为朝着这个方向迈出的重要第一步,iOS 14 和 MacOS Big Sur 包括对使用 URLSession的应用程序的 HTTP/3 支持的实验性预览,可以在开发者选项中启用它。
也可以通过实验设置在 Safari 中尝试相同的 HTTP/3 支持。
本地网络隐私
iOS 14通过为本地网络引入新的隐私保护来改善用户隐私, 这有助于防止应用程序和第三方库或 SDK 使用网络上其他设备来定位用户或追踪用户。与本地网络交互的内置系统服务(例如 AirPrint,AirPlay 和 HomeKit)不会为应用程序提供有关网络的任何私人信息。但是,现在直接访问任何本地网络资源(包括使用多播和广播)都需要明确的用户许可。
如何改造
如果要帮助用户了解你的应用程序如何使用其本地网络,你需要在 info.plist 中为你的应用程序提供一个说明原因的字符串。
用户可以在设置中更改本地网络的权限。
为了更好的用户体验,你可以:
延后获取本地网络权限的时间
避免冷启时浏览本地网 等待用户响应 提供清晰的使用说明
受限网络中的推送连接
在 iOS 14 以前,向 iOS 设备推送消息时,业务服务器需要先将消息发送给 APNs(Apple's Push Notification service) 服务器,APNs 服务器再将消息推送给目标设备。因为需要和 APNs 通信,推送服务依赖于网络连接。
然而部分 APP 严格依赖于消息推送,即使在无网的情况下,也需要能正常使用推送能力。iOS 14 苹果在 NetworkExtension 中增加了本地受限网络中进行推送的 Local Push Connectivity 的 API 。
下面是使用系统的 Push Notification 和新增的 Local Push Connectivity 的比较:
关于如何使用 Local Push Connectivity 的新 API 开发用于受限网络下接收推送的 NetworkExtension,可以参考Build local push connectivity for restricted networks[21]。
由于每台设备只需要和 APNs 建立一条连接就能适用于所有该设备上应用的推送功能,在能使用 APNs (连接可用网络)时,苹果依然推荐使用 APNs。
总结
这篇 session 讨论了如何利用 IPv6,HTTP / 2,TLS 1.3,MPTCP 和加密 DNS 为用户提供性能、安全性、移动性和隐私的好处。Apple 平台上的现代网络 API 当前已经支持所有这些技术,因此客户端只需要确保在应用程序中使用 URLSession 或 Network.framework。接下来,查看服务器部署,以确保所有内容都是最新的,并且已启用这些功能。最后,启用实验性的 HTTP/3 支持,并测试服务器部署,以提供有关下一代网络协议的反馈。
推荐阅读
关注我们
我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。
另外,老司机技术周报周边商店 正式上线,欢迎大家前往订购~
支持作者
这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 102 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~
WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。
参考资料
RFC 2460: https://tools.ietf.org/html/rfc2460
[2]https://www.ibm.com/support/knowledgecenter/zh/ssw_ibm_i_72/rzai2/rzai2compipv4ipv6.htm: https://www.ibm.com/support/knowledgecenter/zh/ssw_ibm_i_72/rzai2/rzai2compipv4ipv6.htm
[3]RFC 4941: https://tools.ietf.org/html/rfc4941
[4]RFC 6434: https://tools.ietf.org/html/rfc2460
[5]RFC 4301: https://tools.ietf.org/html/rfc4301
[6]RFC 7872: https://tools.ietf.org/html/rfc7872
[7]RFC 7540: https://tools.ietf.org/html/rfc7540
[8]RFC 2246: https://tools.ietf.org/html/rfc2246
[9]RFC 4346: https://tools.ietf.org/html/rfc4346
[10]RFC 5246: https://tools.ietf.org/html/rfc5246
[11]RFC 8446: https://tools.ietf.org/html/rfc8446
[12]RFC 6824: https://tools.ietf.org/html/rfc6824
[13]RFC 8684: https://tools.ietf.org/html/rfc8684
[14]https://multipath-tcp.org : https://multipath-tcp.org
[15]anonymyster.com: http://anonymyster.com
[16]RFC7858: https://tools.ietf.org/html/rfc7858
[17]RFC8310: https://tools.ietf.org/html/rfc8310
[18]RFC8484: https://tools.ietf.org/html/rfc8484
[19]RFC8094: https://tools.ietf.org/html/rfc8094
[20]DNSCurve: https://tools.ietf.org/html/draft-dempsky-dnscurve-01
[21]Build local push connectivity for restricted networks: https://developer.apple.com/videos/play/wwdc2020/10113/?time=206