这几年摸了不少SSL相关,觉得同事们做的分享都不是很满意,自己试着写一个。
缘起
MITM
一开始是上大学的时候吧,可能是受育碧游戏《看门狗2》的影响,哇一个手机一键黑入各个系统,超帅。 然后找了一下偶尔看见可以实现网络入侵的app,叫啥?忘了……想起来补一下. 里面有一个功能叫MITM(Man-In-The-Middle),中间人攻击。
怎么玩呢,就是我在接入一个wifi的情况下,在空中劫持别人所有的上网请求,然后伪造一个response回去。 比如我试过把别人请求的所有图片,全部替换成某一张表情包。 没感觉到有多好玩但是真的很危险,因为我很不喜欢这世上有我掌控不了的东西。
OK那么这时候其实对SSL还是没什么概念,只知道网络请求是可以被拦截、被伪造的。
建站
大三大四的时候一边复习考研(最后考太差了),一边手上开始有点积蓄就琢磨搭建我自己的个人主页。 之前上网都是随便玩玩,知道https比http多了一个secure的概念,但也只是知道。 到自己建站的时候想enable https,才知道:哦,要向签证中心申请证书~
好起来了,有了证书,那SSL就来了。
介绍
什么是 SSL/TLS
(此处应有 BGM)
先给一句我自己的定义:
SSL/TLS 是一套协议,让两个从没见过面、还隔着一条谁都可能偷听的线路的人,先花几百字节谈成一件事——接下来用什么密码说话——然后开始说悄悄话。
拆开就是三件事,讲课的顺序千万别搞反:
- 机密性:中间人看得到流量的长度和节奏,看不到内容;
- 完整性:改一个字节,接收方就知道(HMAC / AEAD 里的 tag);
- 身份认证:跟我说话的那个家伙,真的是
example.com吗?
只加密不认证,等于对着一个自称是你朋友的人说心里话——这就是为什么单纯”上个加密”没有意义。
顺便,名字是个历史包袱:SSL 是 Netscape 时代的老名字(2.0 / 3.0),后来 IETF 接手改名 TLS(1.0 / 1.1 / 1.2 / 1.3)。现在说”SSL 证书””SSL 加密”,其实全是 TLS,只是叫顺嘴了。真要说规范,SSL 2.0/3.0 已经死了,别再用。
非对称加密这么优雅,为什么还要对称
因为贵。
RSA / ECC 的运算比 AES 慢好几个数量级,拿它加密视频流,CPU 会先哭。所以真正的分工是这样的:
- 握手阶段用非对称(或密钥交换)把会话密钥谈出来;
- 之后所有数据都用对称加密(AES-GCM / ChaCha20-Poly1305)跑。
握手的目的不是”加密数据”,而是”在不安全的信道上,安全地协商出一把只有两个人知道的临时钥匙”。想清楚这一点,整个协议就不再神秘了。
另外补一句这几年的变化:RSA 密钥交换基本该淘汰了(前向安全性差、还怕量子),现在标配是 ECDHE + X25519——每次会话都换一把临时钥匙,私钥哪天泄露也解不开历史流量。
握手其实是一场谈判
把握手理解成”商务谈判”就好懂:双方先对暗号(版本)、再看能接受哪些条件(加密套件)、互相验证身份(证书),最后交换筹码(密钥材料)。
TLS 1.2:两次往返(2-RTT)。
ClientHello(我支持这些版本/套件,这是我的随机数)→ ServerHello + 证书 + 密钥交换参数 → 客户端验证书、算 pre-master、加密发回 → 双方 Finished。
TLS 1.3:一次往返(1-RTT),会话恢复还能 0-RTT。 它做的事其实是”减法”:把 RSA 密钥交换、CBC 模式、RC4、SHA-1 这些不该存在的东西全砍了,握手报文虽然明文可见,但被 AEAD 保护。所以它又快又安全——不是因为加了什么,是因为删了什么。
0-RTT 有个坑:早期数据可被重放。用在幂等请求上还行,用在”下单””转账”上就是事故。
证书到底在证明什么
一句话:证书只证明”这个域名,在这个时间段内,对应这把公钥”,且这个声明由一个被信任的 CA 签过字。
信任链是这样的:
1 | 系统/浏览器内置的根证书(Root CA) |
浏览器验的不是”你行不行”,而是”能不能一路签名签到我信任的某个根”。所以:
- 中级证书没配:桌面浏览器可能靠缓存/AIA 自动补链,看起来一切正常;某些安卓、某些客户端就直接报错——这就是经典的”我这儿能开,别人那儿打不开”;
- 证书不等于安全:钓鱼站也能申请到证书,
https+ 小锁只是说”连接没被改”,不说”这站不骗人”; - SNI:一台 IP 挂几十个域名,靠 SNI 在握手时告诉服务器你要哪个证书。TLS 1.3 之后 SNI 默认还是明文,所以有了 ECH(加密客户端问候)这种在路上的东西——顺便,这也是”域名前置”为什么曾经有效的原因。
有了 TLS,为什么还能被中间人
这是当年我最想不通的点:既然加密了,凭什么还能劫持?
答案很朴素:MITM 从来不是”破解加密”,而是”骗你的客户端信任我的证书”。
攻击者站在中间,跟服务器说”我是客户端”,跟你客户端说”我是服务器”,然后掏出自己签的证书。理论上浏览器会报警——除非:
- 攻击者把你的根证书装进了系统(企业代理就是这么干的,合规审计的常见姿势);
- 你点过”继续前往(不安全)”;
- 站点没有 HSTS,被降级到 http。
反过来,我大学时替换别人网页图片那件事为什么那么丝滑?因为那些请求本身就是 http,从头到尾没有加密这回事。
所以判断中间人的正确方法是看证书链和主机名,不是看有没有”加密”。顺手记一条:curl -v 里那行 SSL certificate verify result 比什么都诚实。
手边能敲的几个命令
1 | # 看对端到底用了什么版本/套件,以及证书信息 |
浏览器报错的分类,记住四个就够用:
| 报错 | 大概率原因 |
|---|---|
ERR_CERT_AUTHORITY_INVALID |
链不对 / 自签 / 根证书没装 |
ERR_CERT_COMMON_NAME_INVALID |
域名不匹配(含 SAN 里没写) |
ERR_CERT_DATE_INVALID |
过期,或本机时间不对 |
ERR_SSL_VERSION_OR_CIPHER_MISMATCH |
套件谈不拢(老服务只开 TLS 1.0 之类) |
几个我踩过或者常见到的坑
- 证书过期没人管:Let’s Encrypt 90 天,续期自动化没做,某天全站红。acme.sh / certbot + 到期告警,别靠人记。
- 本机时间漂移:现场机器时间不对,证书校验必失败,第一眼容易怀疑到网络上去。排证书问题前先
date。 - 只配了叶子证书没配链:桌面正常、移动端挂,最难查的一类。
- 混合内容:页面走了 https,里面还引着 http 的脚本和图片;主动内容直接被拦,被动内容给个警告,用户看你就”半个小锁”。
- HSTS 开了回不去:
includeSubDomains一开,所有子域都得有证书,想撤还得等 max-age 过期。上线前想清楚。
收尾:如果真要我讲这堂课
讲 TLS 最容易犯的错,是上来就讲数学:大素数、椭圆曲线、离散对数。听众三分钟之内就会掉线,然后全程陪笑。
我现在的讲法会是这个顺序:
- 先讲为什么需要它——就是文章开头那条线:我可视化地劫持过别人的流量,才知道裸奔的世界长什么样;
- 再讲它怎么解决的——握手是一次谈判,证书是一次身份核对,加密只是谈判的产物;
- 最后讲它解决不了什么——钓鱼、拖库、业务漏洞、你自己的弱口令,它一概不管。
落一句总结:TLS 是”让陌生人在不安全的世界里说悄悄话”的工程妥协,不是安全本身。 真正的安全,从来不只在协议里。