TLS 握手
上一步通道打通了,但这条通道是敞开的——路上任何一台设备都能看见你说什么,甚至能改。这一节要在这条通道上再套一层加密壳,并且解决一个更难的问题:怎么确认对面那台机器真的是它自称的那个。地址栏那把小锁,就是这一节的成果。
你按着地址找到了一家"银行",门开着,柜员坐在里面。你正要报出账号和密码——等一下,你凭什么相信这是真的银行?
可能它是真的。也可能这栋楼昨天刚被人租下来,装了一模一样的招牌、印了一样的单据、柜员穿着一样的制服。
你怎么验?看它墙上挂的营业执照。但假的也能印一张执照。所以真正管用的是:执照上有工商局的公章,而那个公章你有办法验真。
验完身份还有第二个问题:大厅里坐着一堆人。你把密码念出来,旁边全听见了。所以你和柜员还得先约定一套只有你俩懂的暗语,后面的对话全用暗语说。
TLS 握手干的正是这两件事:验明对方身份(靠证书链)+ 约定一把只有你俩知道的钥匙(靠密钥协商)。而最神奇的地方在于——这套暗语是在所有人都听得见的大厅里当众商定的,旁听者却推不出来。这一节会讲清这是怎么做到的。
上一步刚完成了什么,这一步要干什么
接力棒交到这一节手上时,手里有的是:一条已经建好的 TCP 连接(§ 4.3 的产物)。这条连接能可靠地传字节——不丢、不重、按序,但完全不保密,也完全不验证对方身份。
为什么必须补上这两件事?因为一条纯 TCP 连接有三个致命缺口:
- 能被看。路上每一台设备(你家路由器、运营商机房、跨国骨干网)都能读到明文内容。你输的密码、看的页面、发的消息,一路上有多少双眼睛谁也说不清。
- 能被改。更严重的是篡改——中间设备可以往网页里插广告、把下载的安装包换成带毒的版本,你完全察觉不到。
- 能被冒充。DNS 可能被劫持(§ 4.2 讲过),你连到的 IP 可能压根不是那家网站的服务器。而 TCP 握手成功只证明"那台机器在",丝毫不证明"它是谁"。
所以这一节要干三件事,一件都不能少:
| 目标 | 专业说法 | 换成大白话 | 靠什么实现 |
|---|---|---|---|
| 别人看不见 | 机密性(Confidentiality) | 说白了就是"说话用暗语,旁人听不懂" | 对称加密(双方共用一把钥匙) |
| 别人改不了 | 完整性(Integrity) | 说白了就是"内容被动过一个字,我立刻知道" | 消息认证码 / 带认证的加密 |
| 对方不是冒充的 | 身份认证(Authentication) | 说白了就是"你得拿出别人验得了真的执照" | 数字证书 + 证书链 |
这三件事里最容易被忽略、也最关键的是第三件。因为前两件如果没有第三件兜底,就毫无意义——你跟一个骗子建立了一条完美加密的通道,加密只保证了没有第三个人偷听你俩,而骗子本人当然全听得见。
干完之后交给下一节的东西是:一条加密通道,加上一个已经验明身份的对端。此后所有 HTTP 报文都从这条通道里过,路上谁都看不懂。
先把这一节的术语翻译成人话
这一节的术语密度是全章最高的,而且几乎全是密码学词汇。别慌——它们本质上都是"锁、钥匙、印章、执照"这几样东西的变体。先过一遍这张表:
| 术语 | 换成大白话 | 生活里对应的东西 |
|---|---|---|
| TLS(Transport Layer Security,传输层安全协议) | 说白了就是"在普通通道外面套的那层保险管道" | 普通信 vs 挂号加密邮件:内容封在专用封套里,还能验寄件人 |
| SSL(Secure Sockets Layer,TLS 的旧名) | 说白了就是 TLS 的老名字,早已被取代但称呼留下来了 | 某个机构改了名,但老百姓还叫老名字 |
| HTTPS | 说白了就是"HTTP 装进 TLS 管道里跑",不是另一个协议 | 同一封信,从平邮换成挂号加密件——信的内容格式没变 |
| 对称加密(Symmetric Encryption,加解密用同一把钥匙) | 说白了就是"同一把钥匙既能锁也能开" | 家里的钥匙:一把钥匙锁门也开门,快,但得先把钥匙交到对方手上 |
| 非对称加密(Asymmetric Encryption,公钥私钥一对) | 说白了就是"一把只能锁、一把只能开" | 小区的信箱:投信口人人能投(公钥),但只有住户的钥匙能打开取信(私钥) |
| 公钥(Public Key) | 说白了就是"到处公开都没关系的那一半" | 你家信箱的投信口——谁都能看到、谁都能用 |
| 私钥(Private Key) | 说白了就是"绝不能给别人的那一半" | 信箱那把钥匙——给出去就等于把信箱交给别人 |
| 数字证书(Certificate) | 说白了就是"一张写着'这个公钥属于这个域名'的官方证明" | 银行墙上挂的营业执照:证明"这家店确实是这家公司" |
| CA(Certificate Authority,证书颁发机构) | 说白了就是"给证书盖章的那个权威机构" | 工商局:它盖的章大家都认,所以执照才有意义 |
| 证书链(Certificate Chain) | 说白了就是"我的章是他盖的,他的章是更上面那位盖的" | 分公司的证明由总公司背书,总公司的资质由国家机构背书 |
| 根证书(Root Certificate) | 说白了就是"这条链条最顶上那个不需要别人证明的" | 国家机构自己的印章——它就是最终标准,没有更上级 |
| 数字签名(Digital Signature) | 说白了就是"用私钥盖的一个只有本人能盖、谁都能验的章" | 手写签名加骑缝章:别人模仿不了,但人人可以拿去比对 |
| 密钥协商(Key Exchange / Agreement) | 说白了就是"当众商定一句只有咱俩懂的暗语" | 两人在嘈杂会议室里,用一套办法约定暗号,旁听者听全了也推不出来 |
| 会话密钥(Session Key) | 说白了就是"这一次通话专用的那把钥匙,挂了就废" | 一次性的临时门禁卡,当天有效 |
| 加密套件(Cipher Suite) | 说白了就是"这次用哪套加密方案的组合清单" | 点菜的套餐组合:主菜+配菜+汤,整套一起定 |
| SNI(Server Name Indication,服务器名称指示) | 说白了就是"我进门先说清我要找哪家店" | 一栋楼里有二十家公司共用大门,你进门得先跟保安说找哪家 |
| 前向保密(Forward Secrecy) | 说白了就是"就算以后我的钥匙被偷了,也解不开以前录下的通话" | 每天换一次门牌密码,且旧密码不能反推——今天的锁被撬也翻不出上月的账 |
这张表里最该先弄明白的是对称加密与非对称加密的分工,因为整个 TLS 的设计就建立在它们的取舍上。对称加密快但要先把钥匙送到对方手里;非对称加密不用送钥匙但慢得多。TLS 的解法非常聪明:用慢的那套去安全地商定一把钥匙,然后用快的那套来传实际数据。
换成大白话:先用一趟郑重的、慢的、有保障的流程把暗语定下来,之后的所有对话就用这个暗语飞快地说。如果全程都用慢的那套,网页会慢到不可用;如果全程都用快的那套,第一步"怎么把钥匙给对方"就无解。
非对称加密是这一节最抽象的概念,但有一个类比能让它立刻变得具体:小区门口那排信箱。
投信口是公开的——它就在楼下大厅,任何人经过都能看到,任何人都能往里塞东西。这就是公钥:可以印在名片上、贴在网站上、发给全世界,一点风险都没有。
但打开信箱的钥匙只有住户有。这就是私钥:一旦交给别人,整个信箱的保密性就归零了。
这个结构带来一个非常反直觉但极重要的性质:"能锁"和"能开"被彻底分开了。你可以让全世界都有能力给你上锁,同时保证只有你能解开。在这之前,人类几千年的加密史里,能加密就等于能解密——这一个分离是二十世纪最重要的发明之一。
而数字签名是这个结构反过来用:用私钥"盖章",用公钥"验章"。因为只有你有私钥,所以只有你盖得出这个章;而因为公钥人人都有,所以人人都能验证"这章确实是他盖的"。相当于一枚全世界都能查验真伪、却只有你一个人能刻出来的印章。
证书的整套机制就建立在这上面:CA 用它的私钥给网站的证书盖章,你的浏览器用 CA 的公钥去验这个章。章验得过,就说明这份证书确实是那个 CA 签发的,不是伪造的。
TLS 1.3 握手:一个来回办完所有事
现在看真实流程。TLS 1.3(由 RFC 8446 定义)把握手压到了一个来回(1-RTT),这是它相对 1.2 最大的进步。完整过程是这样:
你的浏览器 服务器
(TCP 连接已建好) (TCP 连接已建好)
│ │
│ ① ClientHello │
│ ─────────────────────────────────────────→ │
│ 带上:· 我支持 TLS 1.3 │
│ · 我支持这些加密套件(一份清单) │
│ · 一个随机数 │
│ · SNI:我要访问 shop.example.com │
│ · ★ 密钥共享:我这一半的临时公开值 │
│ │
│ ② ServerHello + 证书 + Finished │
│ ←───────────────────────────────────────── │
│ 带上:· 就用 TLS 1.3,加密套件选这个 │
│ · 一个随机数 │
│ · ★ 密钥共享:我这一半的临时公开值 │
│ · 我的证书(以及中间证书,组成证书链) │
│ · 用私钥做的签名(证明我真持有这份证书) │
│ · 从这条消息的后半段开始,内容已经是加密的 │
│ │
│ ③ (本机计算)验证证书链、算出会话密钥 │
│ │
│ ④ Finished(已加密) │
│ ─────────────────────────────────────────→ │
│ "我验完了,密钥算好了,可以开始了" │
│ ★ 这个包后面可以直接跟上 HTTP 请求 │
│ │
加密通道就绪 加密通道就绪
耗时:① 到 ② 是一个完整来回(1 RTT)。
④ 不需要等回应,可以跟第一个 HTTP 请求一起发。
所以 TLS 1.3 的握手成本 = 一个 RTT。
这里最巧妙的设计是第 ① 步就把自己那一半的"密钥材料"发出去了。TLS 1.2 的做法是先商量用什么算法、等对方回复了再交换密钥材料,所以要两个来回。TLS 1.3 的思路是:我先猜你大概会选哪套方案,把材料一次带上;猜对了就一个来回搞定。
换成大白话,这个区别相当于两种去银行办事的方式:
- 旧做法(1.2):你去窗口先问"办这个业务要带什么材料?"柜员告诉你要带三样。你回家取,再来一趟交上去。两趟。
- 新做法(1.3):你事先估计要带什么,第一次去就把可能要的材料全带上。柜员说"对,就要这几样",当场办完。一趟。
代价是你可能白带了几样材料(如果猜错了服务器支持的方案,还得多一个来回补救),但绝大多数时候能猜对,所以平均下来省了整整一个来回。这种"乐观预测 + 猜错才回退"的设计手法在协议优化里非常常见。
TLS 1.2 与 1.3:差的不只是一个来回
把两个版本摆在一起对比,能看出协议演进的完整思路。1.3 不是给 1.2 加功能,而是大幅删减——它砍掉了几十年积累下来的历史包袱。
| TLS 1.2 | TLS 1.3(RFC 8446) | 为什么改 | |
|---|---|---|---|
| 完整握手轮次 | 两个 RTT | 一个 RTT | 客户端第一条消息就带上密钥材料,不再"先商量后交换" |
| 会话恢复 | 约一个 RTT | 支持 0-RTT(重连时可在第一个包里就带数据) | 老客户重连不该再等一个来回 |
| 可选的密钥交换方式 | 很多种,包括已知不安全的 | 只保留基于椭圆曲线等现代方案的少数几种 | 选择越多,配错的机会越多——历史上多数 TLS 事故源于配了不该用的套件 |
| 前向保密 | 可选(不少部署没开) | 强制 | 私钥泄露就能解密历史流量,这个风险太大,不该是可选项 |
| 握手内容是否加密 | 大部分明文,包括证书 | ServerHello 之后就开始加密,证书也被加密 | 连"你在访问哪个站"这类信息也该少泄露 |
| 过时算法 | 还允许一批老算法 | 全部移除 | 兼容老东西的代价是给攻击者留了降级空间 |
这张表里最值得体会的是第三行和第六行:TLS 1.3 的主要安全提升,来自"删掉选项"而不是"增加强度"。
为什么删选项能提升安全?因为真实世界里绝大多数安全事故不是"算法被破解了",而是"有人配错了、或者被诱导降级到了一个弱方案"。打个比方,这相当于一台机器上有二十个按钮,其中三个是危险操作。你可以贴警告标签、写操作手册、培训员工——但只要按钮还在,早晚有人按错。最有效的办法是把那三个按钮直接拆掉。
这个思路值得记住,它在安全设计里叫"减少攻击面":说白了就是"能删掉的功能就是最安全的功能"。同样的道理也解释了为什么 § 4.1 讲的 HSTS 要"不给明文请求任何机会"——不是加强明文的防护,而是让明文压根不发生。
0-RTT:重连时连一个来回都不等
TLS 1.3 还有一个更激进的模式叫 0-RTT(Zero Round Trip Time,零往返时间)。它的意思是:如果你以前连过这台服务器,重连时可以在第一个包里就把数据发出去,不等任何回应。
原理是服务器上次会给你一张"票"(Session Ticket,会话票据),里面加密存着上次协商的信息。你下次带着这张票来,双方就能直接复用之前的密钥材料。
相当于健身房的老会员:第一次去要填表、拍照、办卡(完整握手);此后每次刷卡进门,连招呼都不用打(0-RTT)。
但 0-RTT 有一个必须知道的固有缺陷,而且这个缺陷是设计上无法完全消除的:0-RTT 发出的那份数据可能被重放。
攻击者的做法:
① 在路上把你那个 0-RTT 数据包(连同票据)原封不动地复制下来
② 过一会儿,把这个完整的包再发一次给服务器
③ 服务器无法判断这是不是你本人又发了一次
★ 为什么无法判断?因为 0-RTT 的整个卖点就是"不需要来回确认"。
而"确认对方是活的、这不是录音重播"这件事,
本质上就需要一个来回(服务器发个随机数、你用它回一次)。
——不等来回,就必然失去这种确认能力。
换成大白话:这相当于用录音开门。你对着门喊一句暗号就能进,方便得很;但有人在旁边录了音,回头放一遍,门也开了。要防住录音重播,唯一办法是让门先随机问一句"今天几号"——而那就多了一个来回,0-RTT 的意义也就没了。
所以规范和实践上的处理办法是限制 0-RTT 能用来干什么:只允许发送"重复执行也无害"的请求。比如"给我看看首页"这种,重放一百次也只是多读了一百次;而"给我转账一千块"这种绝对不行,重放一次就多转一千。
这个区分在技术上有个专门的词叫幂等(Idempotent,"做一次和做多次结果相同")。相当于"按一下电梯上行键"是幂等的(按十次也是上一次),而"从柜台取五百块"不是幂等的(做十次就少了五千)。0-RTT 只适合前者。这也是 § 4.5 会讲到的 HTTP 方法幂等性的实际用途之一。
证书链验证:那把小锁背后的完整逻辑
现在讲这一节最核心、也最容易讲糊的部分:浏览器凭什么相信这份证书?
先看一份证书里到底有什么。它本质上就是一张写满字的表格,关键几栏是:
| 字段 | 内容 | 相当于执照上的哪一栏 |
|---|---|---|
| Subject(主体) | 这份证书是发给谁的,含域名 | 企业名称与经营场所 |
| SAN(Subject Alternative Name,主体备用名称) | 这份证书对哪些域名有效(可以是多个,也可以是 *.example.com 这样的通配) | 营业执照上列的所有分店地址 |
| Public Key(公钥) | 这个域名的公钥——整份证书的核心就是为了把"域名"和"这个公钥"绑在一起 | 企业的公章样本(大家拿它去比对) |
| Issuer(颁发者) | 是哪个 CA 签发的 | 由哪个工商局核发 |
| Validity(有效期) | 从哪天到哪天有效 | 执照的有效期限 |
| Signature(签名) | 颁发者用自己私钥对上面全部内容做的签名 | 工商局盖的那个骑缝章 |
浏览器的验证过程是四步,缺一步都不算通过:
① 域名对不上?—— 你访问的是 shop.example.com,
那这个域名必须出现在证书的 SAN 列表里。
★ 这是最容易触发警告的一条:证书是给 www.a.com 的,
你却访问 a.com(不带 www),就会报错。
② 过期了吗?—— 当前时间必须在有效期内。
★ 有意思的后果:如果你电脑的时间设错了(比如设成 2050 年),
所有网站都会报证书过期。这是"HTTPS 全站打不开"的一个经典原因。
③ 签名验得过吗?—— 用颁发者的公钥去验这份证书上的签名。
验得过 → 说明这份证书的内容一个字都没被改过,
而且确实是那个颁发者签的。
④ 颁发者可信吗?—— 这是最关键的一问。
颁发者也有自己的证书,那份证书又是谁签的?
于是往上追一层,再验一次第 ③ 步……
一直追到某一份"根证书",
而根证书就存在你的操作系统或浏览器里,是预置信任的。
★ 这条一层层往上追的路径,就叫「证书链」。
只要链条上任何一环验不过,整条链就废,浏览器立刻报警。
一条典型的证书链有三层:
根证书(Root CA)
│ 它的私钥签发了 ↓ ← 这一份预装在你的设备里,是信任的起点
中间证书(Intermediate CA)
│ 它的私钥签发了 ↓ ← 服务器会连同自己的证书一起发给你
网站证书(shop.example.com)
← 就是这一份把域名和公钥绑在了一起
为什么要多一层"中间证书",不让根证书直接签?因为根证书的私钥太宝贵了——它是全世界信任的起点,一旦泄露,整个体系崩塌。所以正规做法是把根私钥锁在离线的保险设施里,几乎不动它,日常签发工作交给中间 CA。
换成大白话:这相当于国家最高印章不会天天拿出来盖普通文件。它只盖一次——用来授权省级机构;省级机构再去盖那千千万万份日常文件。好处是:如果某个省级机构出了问题,只要吊销它那一份授权就行,不需要动最高印章、也不影响其他省。而如果所有文件都用最高印章直接盖,那一旦最高印章有问题,全国所有文件同时作废。
假设你要验证一位陌生人的身份,他递给你一张工牌,上面写着"某公司 · 技术部 · 李建国"。
你会怎么验?你不认识这个人,也不认识这家公司。但你可以问:"这张工牌是谁开的?"他说是某公司人事部。"人事部凭什么?"因为这家公司在工商局注册过。"工商局凭什么?"——到这里你就停了,因为工商局是你本来就认的机构,不需要再往上问。
这就是证书链的全部逻辑:一路往上问"谁给你背书",直到问到一个你本来就信的人为止。
而"你本来就信谁"这件事,在浏览器里是预置的——你的操作系统和浏览器里装着一份根证书清单(通常一两百份),那些机构就是"你本来就认的工商局"。这份清单不是你自己挑的,是系统和浏览器厂商替你挑的。
这带来一个非常重要、也常被忽略的推论:HTTPS 的安全性,最终建立在"你相信设备里那份根证书清单没被动过"这个前提上。
所以如果有人能往你的设备里塞一份自己的根证书(比如公司发的电脑上装的管理软件、某些声称"能加速上网"的工具),那他就能给任何域名签发一份浏览器认可的证书,你的所有 HTTPS 流量他都能看。相当于有人偷偷往你脑子里塞了一句"某某民间组织也算工商局"——从此他印的执照你全都认。
这就是为什么"随便安装根证书"是极高风险的操作,也是为什么公司电脑上的加密流量并非对公司保密。
密钥协商:在人人都听得见的地方约定暗语
这是 TLS 里最让人惊奇的一部分:你和服务器在一条所有人都能监听的通道上对话,最后却得出了一个只有你俩知道的数。怎么可能?
完整的数学不在本章展开(那属于密码学),但它的结构可以用一个类比讲得很清楚。它的名字叫 Diffie-Hellman 密钥交换(以两位发明者命名,1976 年提出)。
打个比方,用调颜色来说明:
你和对方要在众人围观下约定一个"只有你俩知道的颜色"。
① 你俩公开商定一个起始色:黄色。(所有人都听到了:黄色)
② 你私下选一个秘密色:红色。 (只有你知道)
对方私下选一个秘密色:蓝色。(只有他知道)
③ 你把「黄 + 红」调好,得到橙色,公开寄给对方。
对方把「黄 + 蓝」调好,得到绿色,公开寄给你。
(所有人都看到了:橙色和绿色)
④ 你把收到的绿色,再加上你的秘密红色 → 得到「黄+蓝+红」
对方把收到的橙色,再加上他的秘密蓝色 → 得到「黄+红+蓝」
★ 两边得到的是同一个颜色!
⑤ 而旁观者手上只有:黄色、橙色、绿色。
他要算出「黄+红+蓝」,就必须先从橙色里把红色分离出来——
而"把调好的颜色重新分解成原料"这件事极其困难。
这就是全部诀窍:混合容易,分离极难。
真实的数学里,"混合"是模幂运算,"分离"是离散对数问题——前者电脑瞬间算完,后者用现有算力在合理时间内做不到。整个现代加密体系就建立在这类"正着算容易、反着算极难"的问题上。
换成大白话:这相当于把一杯咖啡和一杯牛奶倒在一起——一秒钟就混好了,但要把它们重新分开,难度是另一个量级。所有人都看到你倒了什么进去混合物,却没人能把混合物拆回原料。
这套机制还带来一个极重要的性质,叫前向保密(Forward Secrecy):因为每次连接用的"秘密色"都是临时随机生成、用完就扔的,所以即使将来服务器的私钥被偷了,攻击者也解不开他之前录下的通话内容。
相当于你和对方每次见面都现场约定一个新暗语,而且约完就把纸条烧掉。就算十年后有人偷到了你家的钥匙,也翻不出这些对话——因为那些暗语从来没有被存在任何地方。TLS 1.3 把前向保密变成了强制而非可选,这是它安全性上最大的提升之一。
SNI:一栋楼二十家公司,你得先说找哪家
握手的第一条消息 ClientHello 里有一个字段叫 SNI(Server Name Indication,服务器名称指示),内容就是你要访问的域名。为什么要在这里再说一遍域名?不是已经通过 DNS 找到 IP 了吗?
因为一个 IP 上常常住着很多个网站。共享主机、CDN 节点、云服务器上一个 IP 挂几十上百个域名是常态。服务器收到握手请求时面临一个难题:我该拿哪份证书给他?我这儿有二十份证书,分属二十个不同域名。
这是个死结:证书要在握手时发出去,可"该发哪一份"这个信息在 HTTP 请求里(Host 头),而 HTTP 请求要等握手完成、加密建立之后才能发。鸡生蛋、蛋生鸡。
SNI 就是解这个死结的:在握手的第一句话里,明文告诉服务器"我要找的是 shop.example.com",服务器就知道该拿哪份证书了。
换成大白话:这栋写字楼里有二十家公司共用一个大门。你进门时保安问"找哪家",你得先说清,他才知道该带你去哪层、该出示哪家的接待流程。你不能进门就要求"先给我看你们的营业执照"——保安会反问"你要看哪家的?"
但 SNI 有一个至今存在的隐私缺口:它是明文的。也就是说——即使你的所有通信内容都被加密了,"你正在访问哪个域名"这件事仍然暴露在路上。
| 信息 | HTTPS 下路上的人能看到吗 | 说明 |
|---|---|---|
| 你访问的域名 | 能(通过 SNI,也可能通过 DNS 查询) | 这是最大的隐私缺口 |
| 目标服务器的 IP | 能 | 包头上就是明文,必须如此才能路由 |
| 你访问的具体路径 | 不能 | /item/8848 在加密内容里 |
| 你提交的账号密码 | 不能 | 在加密内容里 |
| 返回的页面内容 | 不能 | 在加密内容里 |
| 传输的数据量与时间模式 | 能(间接推测) | 加密隐藏内容,但隐藏不了"有多少、什么时候" |
这张表值得记住,因为它精确划出了 HTTPS 保护的边界。一个常见误解是"用了 HTTPS 别人就完全不知道我在干什么"——不对,别人知道你去了哪家店,只是不知道你在店里买了什么。
相当于你走进一家挂号大厅,外面的人看得见你进了哪个科室的门,但听不见你和医生说了什么。"去了哪儿"是暴露的,"说了什么"是保密的。业内有加密 SNI 的方案在推进,但本文不对其部署状况下结论。
证书出问题时浏览器会说什么:五种警告的真实含义
这一节最实用的部分。浏览器的证书警告是普通用户最常遇到、也最常被无脑点"继续访问"的东西。下面五种含义完全不同,其中有的可以忽略,有的绝对不能。
| 警告 | 真实含义 | 危险程度 | 常见的真实原因 |
|---|---|---|---|
| 证书已过期 | 有效期已过 | 中——可能只是网站忘了续,但也可能是你的系统时间错了 | 网站运维忘续期;或你设备时间不对(先去检查这一项) |
| 域名不匹配 | 证书上的域名不含你访问的这个 | 中到高 | 访问了 a.com 而证书只给了 www.a.com;或者你真的连到了别人的服务器 |
| 颁发者不受信任 | 追不到你设备里任何一个根证书 | 高 | 自签名证书(开发环境常见);或有人在中间拦截 |
| 证书已被吊销 | CA 明确宣布这份证书作废了 | 极高 | 该证书的私钥已泄露——继续访问等于明知有诈还上门 |
| 混合内容(Mixed Content) | 页面本身是 HTTPS,但里面引用了 HTTP 资源 | 中 | 网站改造不彻底。危险在于那些 HTTP 资源可被篡改 |
关于"要不要点继续访问",有一条很简单的判断标准:如果这个页面你要输入任何东西(账号、密码、验证码、地址),一律不要继续。如果只是看看公开内容,风险相对低。
换成大白话:只是路过看看橱窗,店铺的执照有点问题也就罢了;但你要往里交钱、报身份证号,那执照必须验得过,一点含糊都不能有。
这里还要澄清一个特别重要的点:"证书有效"和"网站是好人"是两件完全不同的事。证书只证明"这个域名确实归这份公钥的持有者",它不证明这家网站诚实、不欺诈、不卖假货。
相当于一家店的营业执照货真价实,但它可能就是一家专门骗人的店——执照证明的是"它确实是登记的那家公司",不是"它做的生意是好生意"。所以钓鱼网站也可以有一把合法的绿色小锁,只要它注册了一个自己的域名。看到小锁不等于安全,只等于"你确实连到了地址栏上那个域名"——而地址栏那个域名本身可能就是伪装的(这正是 § 4.1 讲的同形字攻击)。
整节小结:TLS 握手的完整时间线
把本节内容按真实顺序串起来,接在 § 4.3 的第 ⑦ 步之后:
接过一条已建好的 TCP 连接(目标 443 端口)
│
├─① 查会话缓存:以前跟这台服务器握过手吗?
│ 有票据 → 走会话恢复,可能是 0-RTT(几乎不花来回)★
│ 没有 → 完整握手,继续
│
├─② 发 ClientHello:我支持的版本、加密套件清单、随机数、
│ SNI(明文的域名)、我这一半的密钥材料
│
├─③ 收 ServerHello:定下版本与套件、服务器随机数、
│ 服务器那一半的密钥材料、证书链、签名
│ —— 这条消息的后半段起,内容已经加密
│
├─④ 本机验证证书链(不占网络时间,占 CPU)
│ · 域名对得上吗(看 SAN)
│ · 在有效期内吗
│ · 每一层签名验得过吗
│ · 最终追到的根证书在我信任的清单里吗
│ 任何一项失败 → 弹警告,流程中断
│
├─⑤ 双方各自用「自己的秘密 + 对方的公开值」算出同一把会话密钥
│ —— 这把密钥从未在网络上传输过,这是整套设计的精髓
│
├─⑥ 发 Finished(加密的),可以跟第一个 HTTP 请求一起发
│
└─⑦ 加密通道就绪 → 发 HTTP 请求 → 这就是 § 4.5 的起点
耗时量级:
会话恢复 / 0-RTT 接近 0 到不足一个 RTT
TLS 1.3 完整握手 约一个 RTT + 一点本机计算时间
TLS 1.2 完整握手 约两个 RTT
(证书验证本身不占网络来回,但要做若干次签名验算,占少量 CPU)
注意第 ① 步那个 ★ 号。和前面几节一样,最优的路径永远是"这活儿我以前干过,直接复用"。四节下来你应该已经看出这条贯穿全章的规律了:DNS 有缓存、TCP 有连接池、TLS 有会话票据、HTTP 有本地缓存——每一层都在拼命避免重复劳动。
说白了,整个网络栈的性能优化史,就是一部"想办法不干第二遍"的历史。
中间人攻击:TLS 到底在防谁
把 TLS 的三个目标放在一起看,你会发现它们共同针对的是同一个敌人:中间人(Man-in-the-Middle,简称 MITM——处在你和服务器之间、能看能改的那个角色)。
先看没有 TLS 时,这个角色能干什么:
正常情况:
你 ←──────────────→ 服务器
有中间人时:
你 ←──→ 中间人 ←──→ 服务器
│
├─ 能看:你发的密码、你看的页面,全部明文
├─ 能改:往页面里插广告、把下载包换成带毒的
└─ 能冒充:他可以对你装成服务器、对服务器装成你
而双方都完全察觉不到
★ 最可怕的是第三条:双方都以为在跟对方直接说话。
你输的密码,他先收下,再转发给真服务器;
真服务器的回复,他看一眼,再转发给你。
从"能不能用"的角度看,一切完全正常。
换成大白话:这相当于你和对方之间站了一个"翻译"。你说的话他翻给对方,对方的话他翻给你,业务办得顺顺利利——但他知道你们说的每一句,而且他随时可以在翻译时悄悄改几个字,你俩谁都不会发现。
那 TLS 是怎么挡住他的?关键不在加密,在证书。这一点值得反复强调,因为它是最常被误解的地方:
| 如果只有加密、没有身份认证 | 如果有证书验证 |
|---|---|
| 中间人跟你建一条加密通道,再跟服务器建另一条 | 他也可以这么试 |
| 两条都是"完美加密"的,没有第三者能偷听 | 但他必须向你出示一份 shop.example.com 的证书 |
| 但中间人本人当然全看得见 | 而他拿不出来——他没有那个域名的私钥,也没有任何 CA 会给他签这个域名的证书 |
| 结果:加密完全无效 | 结果:浏览器报警,流程中断 |
所以"加密"防的是偷听,"证书"防的是冒充。没有证书,加密就是给骗子搭了个隔音包间。
相当于你和"翻译"关在一个隔音会议室里谈——外面的人一句听不见(加密有效),但那个翻译本人当然全知道。要挡住他,唯一办法是先验明"你到底是谁",而不是把房间修得更隔音。
这也解释了为什么 § 4.2 说 hosts 文件劫持在 HTTPS 时代威力大减:攻击者能把你的域名指向自己的服务器(DNS 层面得手了),但他没法在 TLS 层面拿出一份合法证书,所以最终还是过不了这一关。这是分层设计的一个漂亮之处:上一层的失守,可以由下一层补救。
HTTPS 到底"慢"不慢:一个过时的印象
早年有个流传很广的说法:HTTPS 比 HTTP 慢很多,所以只在登录页用就行。这个说法在今天已经基本站不住了,但很多人的印象还停在那里。值得把账算清楚。
HTTPS 相对 HTTP 的额外成本,就三笔:
- 握手的那一个来回。TLS 1.3 是一个 RTT,而且会话恢复几乎不花时间。这是唯一一笔真实、无法完全消除的开销,但它只在建立连接时付一次,之后所有请求都免费。
- 加解密的计算量。早年 CPU 弱、加密指令没有硬件加速时,这一笔很显眼。如今主流处理器普遍带专用加密指令,这笔开销在多数场景下已不显著。
- 证书传输的几 KB 数据。相对现代网页的体积几乎可以忽略。
而反过来,HTTPS 带来的性能好处常常被忽略:现代协议的新特性(HTTP/2、HTTP/3 的多路复用等)在实践中往往只在加密连接上启用。所以在真实网站上,从 HTTP 换到 HTTPS 有时反而更快——因为顺带启用了更先进的传输机制。
换成大白话:过去的印象是"上锁要多花时间",现在的情况是"只有上了锁的门才配了自动感应和快速通道"。算总账反而更划算。
还有一个更根本的理由:"只在登录页用 HTTPS"这个做法本身是不成立的。因为登录之后你的身份凭证(Cookie)会随每个请求发出去,如果后续页面走明文,那凭证就在路上被看见了——相当于开户时锁着门办,办完之后把账号本子扔在大街上。所以现代做法一律是全站 HTTPS,配合 § 4.1 讲的 HSTS 强制执行。
动手看一次真实的握手
这一节讲的所有东西都能亲眼验证。三个动作,从易到难:
第一件:点开地址栏那把小锁。浏览器会显示这次连接用的 TLS 版本、加密套件、以及完整的证书链。值得点开看几次不同的网站——你会发现证书链的层数、颁发机构、有效期各不相同,而"从网站证书一路追到根证书"这条链条会以树状清清楚楚地列出来。
第二件:在开发者工具里看 TLS 耗时。
F12 → Network 面板 → 勾上 Disable cache → 刷新
→ 点第一条请求 → Timing 标签
其中 "SSL"(有的浏览器写 "TLS")那一段就是本节的全部耗时。
值得做的三组对比:
① 访问国内近距离站点 vs 跨国站点
→ SSL 段的差距主要来自物理距离(因为它是一个 RTT)
② 第一次访问 vs 短时间内再访问
→ 第二次这一段常常直接消失(连接复用或会话恢复)
③ 对比 Initial connection 与 SSL 两段
→ 它们通常量级相近,因为都是"一个来回"级别的开销
第三件:故意制造一次证书错误。最安全的做法是把系统时间改到很久以后(比如两年后),然后访问任意网站——你会看到大批网站同时报证书错误。这就验证了前面讲的"有效期检查"那一步是真的在跑,而且它检查的是你本机的时间。看完记得把时间改回来。
这个实验有实际用处:以后遇到"所有 HTTPS 网站都打不开"的情况,第一个该怀疑的就是系统时间不对——而不是重装浏览器。相当于整栋楼所有执照都显示过期,那大概不是二十家公司同时忘了续期,而是你手上那本日历翻错了年份。
这一节在已经打通的通道上做了三件事:让别人看不见(机密性)、改不了(完整性)、以及最关键的——确认对方不是冒充的(身份认证)。前两件如果没有第三件兜底就毫无意义,因为你可能跟骗子建了一条完美加密的通道。
核心机制一 · 公钥与私钥:像小区信箱——投信口公开(公钥),取信钥匙私有(私钥)。反过来用就是数字签名:只有我能盖、人人都能验。整个证书体系建立在这上面。
核心机制二 · 证书链:一路往上问"谁给你背书",直到追到设备里预置的根证书。四步验证缺一不可:域名对不对(看 SAN)、过期没、签名验得过没、最终追到的根在不在信任清单里。中间证书的存在是为了保护根私钥——某个中间机构出事只吊销它那一份,不影响全局。推论:HTTPS 的安全性最终建立在"你设备里那份根证书清单没被动过"这个前提上——所以随便安装根证书是极高风险操作。
核心机制三 · 密钥协商:在人人都能监听的通道上约定出只有双方知道的密钥,靠的是"混合容易、分离极难"(调颜色的类比)。那把会话密钥从未在网络上传输过。而"每次用完就扔"带来前向保密——将来私钥泄露也解不开以前录下的流量,TLS 1.3 把它变成强制。
版本差异(RFC 8446):TLS 1.3 是一个 RTT,1.2 是两个 RTT;1.3 还支持重连时的 0-RTT(但有无法根除的重放风险,所以只该用于幂等请求)。1.3 的主要安全提升来自"删掉选项"而不是"加强算法"——因为事故大多来自配错和降级,而不是算法被破。
两条最该记住的边界:① SNI 是明文的,所以 HTTPS 保护"说了什么",不保护"去了哪儿";② 证书有效 ≠ 网站可信——小锁只证明"你确实连到了地址栏那个域名",钓鱼网站一样能有合法证书。
下一步交出什么:一条已验明身份的加密通道。到这里,前三步的准备工作全部完成——终于可以开口说正事了。§ 4.5 就来看那句正事怎么说、对方怎么答。