What happens when..(network)
当在浏览器输入栏中输入 www.google.com 会发生什么
What happens when#
今天来详看一下「当你在浏览器输入栏中输入 www.google.com ↗ 会发生什么?」世纪经典面试问题的答案,当然这篇文章主要聚焦于计算机网络部分的故事,在这之前还存在着包括硬件驱动等故事,具体 参看 ↗
Parse URL#
URL(Uniform Resource Locator)统一资源定位符。在浏览器输入框内部收到输入的内容后,首先需要判断输入内容是否是一个 URL 还是一个搜索项。
URL 包含着以下信息,在浏览器中:
protocol
“http”Resource
”/”
如果浏览器没有在输入的目标文字中检测到有效的「协议」和「域名」,那么目标文字就会被直接转入到默认的搜索引擎中。在大部分情况下,如果直接使用默认的搜索引擎,浏览器会自动在搜索结果中的 URL 中附加一些特定的信息,包括用于标识这样的搜索请求来自哪一个浏览器等等。
在 chrome 浏览器中输入 erasernoob 搜索,对应的 URL 如下: https://www.google.com/search?q=erasernoob&oq=erasernoob&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIGCAEQRRhBMgwIAhAAGAgYChgNGB4yDQgDEAAYhgMYgAQYigUyBggEEEUYPDIGCAUQRRg8MgYIBhBFGDwyBggHEEUYQdIBCDU2ODNqMGo3qAIAsAIA&sourceid=chrome&ie=UTF-8 ↗
sourceid
:代表地址栏的来源浏览器gs_lcrp
:代表着 chrome 浏览器添加的搜索上下文参数oq
: original query 记录了用户输入的原始内容q
: query 记录最终的搜索参数(包括根据 google 建议补全)
Convert non-ASCII Unicode characters in the hostname#
- 浏览器检查 域名中是否存在不是 ASCII 码的符号
A-Z
a-z
0-9
-
.
**早期,互联网的设计是基于 ASCII 编码的,所以只支持上述的符号。**所以 DNS 智能识别 ASCII 字符,所以如果有一个域名包括了非 ASCII 码,那么就需要通过 PunyCode ↗ 的编码方式转换为合法的 ASCII 码。
PunyCode: Punycode 把非 ASCII 字符转换为数字表示,然后用一种特殊算法压缩成只包含 ASCII 的字符。它保留了原始 ASCII 的部分,只“翻译”非 ASCII 的部分。
你好.com
->xn--6qq79v.com
PunyCode 翻译过后域名以xn--
开头,代表着这是 PunyCode 编码之后的域名
Check HSTS list#
浏览器开始通过查 「预加载 HSTS 列表」 (preloaded HSTS (HTTP Strict Transport Security) list),如果目标网站在这个列表中,那么浏览器就直接通过 HTTPS 方式请求服务器。如果网站不在列表中,那么第一个请求就使用 HTTP 方式。
为什么要使用 HSTS list?#
预加载 HSTS 列表,其中包含着所有浏览器之前保存过的需要用 HTTPS 访问的所有网站。HSTS 是一个 HTTP 安全机制,服务器需要通过响应头设置告诉浏览器:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
lua告知浏览器,之后的一整年(max-age 以秒为单位)都要使用 HTTPS 访问我。所以这样的问题就是,第一次请求还是得用 HTTP 的方式请求,还是会存在着被攻击的风险,所以干脆这样维护一个预加载的列表,增强安全性。
DNS Lookup#
- 浏览器首先检测目标域名是否在缓存中,
chrome://net-internals/#dns
输入来查看 chrome 浏览器的 dns 缓存。 - 如果不在缓存中,那么浏览器调用
gethostbyname
库函数来完成查询 gethostsname
库函数查看目标域名的地址是否存在在本地的hosts
文件中,不同的操作系统该文件位置不同。如果不在,那么就通过会像 DNS 服务器发送一条 DNS 查询解析请求(Linux 的/etc/hosts
或 Windows 的C:\Windows\System32\drivers\etc\hosts
)- DNS 服务器由操作系统配置在网络协议栈(network stack)中,这通常包括本地路由器和 ISP 的缓存 DNS 服务器(这样可以加速 DNS 解析,不用每次都去根域名服务器或者权威服务器进行访问)
- 如果发现 DNS 服务器的 IP 地址和本机在一个子网中,此时 OS 转入到局域网请求流程,通过 ARP(Address Resolution Protocol)协议查询到 DNS 服务器的 MAC 地址,通过 MAC 地址直接将 DNS 请求包发给目标 DNS 服务器
- 若不在一个子网中,那么通过 ARP 协议查询到默认网关的 MAC 地址,将 DNS 请求包发给默认网关
经过 ARP Process
之后,此时要么拿到了 DNS 服务器的 IP 或者是默认网关的 IP,紧接着就可以继续 DNS 查询请求:
- DNS 客户端通过 UDP 53 端口向 DNS 服务器建立 socket 连接发送请求包,如果请求包太大就会使用 TCP
- 如果没有找到,那么 DNS 服务器就会递归的向上层服务器请求,直到抵达授权 DNS 服务器 (SOA)
Opening of a socket#
浏览器一收到目标 IP 地址,便会带着目标 IP 以及目标端口(Http 默认为 80, Https 默认为443)z后调用系统库函数 socket
然后请求使用一个 TCP 套接字流:
- 首先,第一个请求被传递到传输层,在这里请求被封装成 TCP segment,然后目标端口被加入请求头部,源端口由 OS 随机决定
- segment 被送到网络层,在这里会在被加上一个 IP 头部,其中包含着目标 IP 以及本机的源 IP
- 接下来 packet 抵达链路层,在这里加上一个包含着本机的 MAC 以及默认网关的 MAC 的帧头,如上文所说,如果不知道默认网关的 MAC 那么就需要通过 ARP 广播请求到。
到这个时候,请求包 packet 就准备好被
Ethernet
,WiFi
,Cellular data network
三者其中之一方式传送转发了。
对于大部分家庭网络或者小型企业网络,请求包首先会从电脑出发,通过本地网络之后,然后通过一个现代的调制解调器(MOdulator/DEModulator),将数字信号(0/1)转换为模拟信号,这样可以在电话线路等上传播,在线路的另一端会有另外一个调制解调器,相反的,它会将模拟信号转换成原有数字信号将请求包继续向前传播,到达下一个网络节点
现在比较现代的网络大多都直接和以太网相连接,所以请求包数据保持着数字信号一路向前,直接被传向下一个网络节点。
整体上看,数据通过抵达本地子网的路由器,之后继续前往自治系统(AS)的边界路由器,穿越其他自治系统,最终达到目标服务器。
在路上的每一跳(经过的路由器)都会对请求包进行拆封,根据目标 IP 地址,决定下一跳需要将请求包送往哪里。其中 TTL
和 network congestion
都会造成包的丢失。
在上面的请求接收过程中,TCP的连接建立会发生很多次,TCP connection flow :
- 客户端选择一个 ISN (Initial Sequence Number),并设置好 SYN 位,代表正在设置 ISN 位,需要和服务器建立连接
- 如果服务器目前可以接收当前的连接,接收 SYN
- 设置自身的 ISN 位
- 设置 SYN 位,代表其正在设置 ISN 位
- 将接收到的 ISN 位 + 1,并将得到的数字设置在 ACK 字段,并设置 ACK 位,代表确认收到客户端的的数据
- 客户端确认连接并发送数据包
- 客户端将 ISN + 1
- 将服务器发送的 ISN + 1 并设置在 ACK 字段
- 设置 ACK 位,代表确认收到
- 数据开始阐述
- 当一方收到了 N 字节数据,那么 SEQ 字段就加上 N
- 当另一端收到了相应的数据包,就返回一个 ACK 请求确认包,ACK = 上一次收到的数据包的最后一个序列号
- 关闭连接
- 关闭者设置 FIN 位,发送 FIN 请求包
- 接收者收到 FIN 请求包,设置 FIN 位,设置 ACK,发送 FIN 请求包
- 关闭者确认收到接收者发出的 FIN 请求包, 设置 ACK
TLS(Transport Layer Security) handshake#
- 客户端发起握手(ClientHello)
客户端发送 ClientHello
消息,包含:
- 支持的 TLS 版本(如 TLS 1.2 / 1.3)
- 支持的密码套件(Cipher Suites)
- 客户端随机数(Client Random)
- 支持的扩展(如 SNI、ALPN)
- (TLS 1.3)密钥共享(KeyShare)参数
- 服务器响应握手(ServerHello)
服务器返回 ServerHello
消息,包含:
- 选择的 TLS 版本
- 选定的密码套件
- 服务器随机数(Server Random)
- (TLS 1.3)服务器的 KeyShare 参数
-
(TLS 1.2)发送服务器证书及密钥交换信息
Certificate
: 服务器发送数字证书(含公钥)ServerKeyExchange
: 若非 RSA,需要发送 Diffie - Hellman 参数CertificateRequest
: 可选,要求客户端发送证书ServerHelloDone
: 握手阶段结束标志(TLS 1.2)
-
客户端处理服务器消息
- 验证服务器证书的合法性(通过 CA 链)
- 生成预主密钥(Pre - Master Secret)
- 加密发送给服务器(或使用 DH 完成共享)
-
生成会话密钥
- 双方使用:
Pre - Master Secret
Client Random
Server Random
- 计算出会话密钥(Master Secret)
- 再派生出加密/解密所需的对称密钥和 MAC 密钥
-
握手完成
客户端发送:
- ChangeCipherSpec
: 通知服务器将启用加密通信
- Finished
: 加密的握手完成校验信息
服务器发送:
- ChangeCipherSpec
- Finished