十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TLS + Web API 安全 · 01 · TLS 原理与握手

TLS + Web API 安全 · 01 · TLS 原理与握手 一、先看问题HTTP 是明信片HTTP超文本传输协议是浏览器和网站之间说话的语言。它的特点就一个字明文。也就是说你发出去的内容在网络上经过的每一台设备路由器、交换机、Wi-Fi 热点、运营商都能直接看到原文。我们亲手抓一次包看看。在远端执行cd /opt/tls-api-labs # 在后台抓 lo本地回环网卡上 8081 端口的流量写到 /tmp/plain.txt timeout 6 tcpdump -i lo -A -s0 port 8081 /tmp/plain.txt 2/dev/null ​ # 等抓包起来后用 HTTP 明文登录一次 sleep 1 curl -s http://127.0.0.1:8081/api/login -H Content-Type: application/json -d {username:alice,password:password1} /dev/null ​ sleep 6 grep -aE POST|username|password|Host: /tmp/plain.txt | head真实回显账号密码是裸奔的。抓包工具tcpdump、Wireshark不需要任何密钥直接把明文打印出来了。命令逐参数解释tcpdumpLinux 上最常用的抓包工具能把网卡上流过的数据包抄下来。-i lo-i指定网卡interface。lo是本地回环网卡loopback本机进程之间通信走它。我们要抓的是本机发往本机的请求所以用lo。-A以 ASCII 文本形式显示包内容这样明文 HTTP 能直接读出来。-s0-s指定每个包最多抓多少字节snaplen0表示不限制整个包都抓。老版本 tcpdump 默认只抓几十字节如 68会截断应用层内容所以要写-s0。port 8081过滤条件只抓 8081 端口的流量port表示端口。不过滤的话会抓到一堆无关流量。把输出重定向到文件。2/dev/null把错误信息丢掉不显示。结尾的放到后台运行不阻塞当前终端。timeout 6 ...6 秒后自动结束免得它一直抓着。二、TLS 要同时解决三个问题HTTPS HTTP TLS。TLSTransport Layer Security传输层安全在 HTTP 之下、TCP 之上给数据套了一个加密管道。它必须同时解决三个问题缺一不可问题专业名词攻击者能做什么TLS 怎么解决别人能偷看机密性Confidentiality窃听密码、聊天记录加密别人能偷偷改完整性Integrity篡改转账金额、注入恶意内容消息认证码 / 签名别人能冒充网站身份认证Authentication假基站、假 Wi-Fi、DNS 劫持到钓鱼站数字证书只加密不就行了为什么还要完整性和身份认证假设只有加密攻击者虽然看不懂内容但可以把密文乱改一通让你解出来一堆乱码这叫篡改。再假设只有加密和完整性攻击者可以直接冒充你连接的服务器——因为客户端根本不知道对面是谁攻击者用自己的密钥和客户端加密聊天客户端还蒙在鼓里。所以三者必须一起上。三、密码学地基3.1 对称加密一把钥匙锁和开都用它是什么加密和解密用同一把密钥。典型算法AES。打个比方一个带锁的保险箱你用钥匙锁上对方用同一把钥匙打开。优点快。适合加密大量数据比如整个网页。致命弱点密钥怎么安全地给对方你俩如果从没见过面你怎么把钥匙交给他直接把钥匙发过去路上被偷了怎么办这就是密钥分发问题。在 TLS 里对称加密用来加密真正的业务数据因为快。但钥匙的协商要靠下面的非对称加密。3.2 非对称加密公钥和私钥一对钥匙是什么一次生成一对密钥公钥public key可以随便公开谁都能拿到。私钥private key只有自己保管绝不外泄。规则很神奇用公钥加密的东西只有对应的私钥能解开。用私钥签名的东西用对应的公钥能验证见 3.5。打个比方公钥像一个投递口谁都能往里塞信私钥像信箱钥匙只有主人能打开取信。优点解决了密钥分发问题——公钥可以公开传不怕被偷。弱点慢比对称加密慢几百上千倍不适合加密大量数据。TLS 的聪明之处用非对称加密只协商出一把对称密钥之后所有数据都用对称加密。这样既安全又快。3.3 哈希数据的指纹是什么把任意长度的数据算成一个固定长度的短字符串摘要。典型算法SHA-256固定 32 字节。特点单向能从数据算出指纹但无法从指纹反推出数据。敏感数据改一个字节指纹就完全不同。抗碰撞几乎不可能找到两个不同数据有相同指纹。用途校验数据有没有被改比对指纹、存密码存指纹而不是明文。注意哈希本身不能证明是谁因为它不需要密钥任何人都能算。要防篡改还得加密钥于是有了 HMAC。3.4 HMAC带密钥的哈希消息认证码是什么HMAC Hash 一把共享密钥。双方都有同一把密钥发送方算一个带密钥的指纹接收方用同样的密钥再算一遍一致就说明没被改且确实是拿着密钥的人发的。解决了什么完整性 来源可信。攻击者没有密钥改不了数据还伪造不出新指纹。弱点要求双方事先共享同一把密钥又回到密钥分发问题。所以 HMAC 常用在已经通过别的方式建立了共享密钥之后。JWT 里的HS256就是 HMAC-SHA256。后面第 05 篇会大量用到。3.5 数字签名私钥签公钥验是什么发送方用自己的私钥对数据的哈希值签名任何人用发送方的公钥都能验证这个签名。它证明了什么身份只有私钥持有者能签出这个名 → 说明确实是他发的。完整性数据被改了签名就验不过。和 HMAC 的区别HMAC 双方共享一把密钥对称数字签名用私钥签、公钥验非对称所以不需要共享密钥而且能向第三方证明是谁签的。弱点你得先确认这把公钥真的是他的。否则攻击者可以拿自己的公钥冒充说我是银行。这个确认公钥归属的问题就由数字证书来解决第 02 篇整篇讲它。3.6 小结TLS 的组合拳把上面五样拼起来就是 TLS 的核心思想1. 服务器把「公钥」放进一张「证书」证书由权威 CA 签名 → 解决公钥是谁的 2. 客户端验证证书拿到可信的服务器公钥 → 身份认证 3. 双方用非对称加密/密钥交换协商出一把「对称会话密钥」 → 解决密钥分发 4. 之后所有数据用「对称加密」传并用「MAC/签名」保证完整性 → 又快又安全一句话用非对称的方式安全地商量出一把对称钥匙然后用这把对称钥匙快速加密后面的所有对话。四、TLS 1.2 握手一次完整的相亲握手handshake指正式传数据之前双方先协商好用什么加密、密钥是多少、你是谁。TLS 1.2 需要2 个 RTTRTT Round Trip Time一次一去一回的网络耗时。下面是 TLS 1.2 的完整流程→表示客户端发←表示服务器发客户端 服务器 │ │ │ ──① ClientHello ─────────────────────────────│ 我支持的 TLS 版本、加密套件、一个随机数 │ │ │ ─② ServerHello ───────────────────────────── │ 选定的版本、套件、另一个随机数 │ ─③ Certificate ───────────────────────────── │ 服务器证书含公钥 │ ─④ ServerKeyExchange ─────────────────────── │ 密钥交换参数如 ECDHE 的公钥 签名 │ ─⑤ ServerHelloDone ───────────────────────── │ 我说完了 │ │ │ ──⑥ ClientKeyExchange ───────────────────────│ 客户端的密钥交换参数 │ ──⑦ ChangeCipherSpec ────────────────────────│ 接下来我发的都加密了 │ ──⑧ Finished加密───────────────────────── │ 握手校验值 │ │ │ ─⑨ ChangeCipherSpec ──────────────────────── │ │ ─⑩ Finished加密───────────────────────── │ │ │ │ ══════ 之后全是加密的应用数据HTTP══════════ │逐步解释每一步为什么需要① ClientHello客户端先说我支持哪些 TLS 版本、哪些加密套件cipher suites外加一个随机数。这个随机数是后面生成密钥的原料之一每次连接都不一样保证密钥不重复。② ServerHello服务器从客户端给的清单里挑一个版本和套件再给一个自己的随机数。③ Certificate服务器把证书发过来。客户端用内置的 CA 公钥验证它从而确信这把公钥属于我要访问的网站。④ ServerKeyExchange如果用的是 ECDHE 这类密钥交换算法服务器在这里给出密钥交换需要的参数并用私钥签名防止被人中间篡改。⑤ ServerHelloDone告诉客户端我这边说完了。⑥ ClientKeyExchange客户端给出自己那半份密钥交换参数。至此双方各自都能算出一个共享的预主密钥。⑦⑧客户端发 ChangeCipherSpec 说接下来加密然后发一个 Finished把前面所有握手内容的摘要加密发过去——对方一验证就知道前面的握手有没有被篡改。⑨⑩服务器同样回应。为什么是 2-RTT因为客户端要等服务器把 Hello 相关的东西发完第 1 个 RTT再把自己的密钥交换参数和 Finished 发过去第 2 个 RTT。多一个来回就多一次延迟。密钥是怎么算出来的双方各自贡献一个随机数ClientHello 的 ServerHello 的再加上密钥交换算出的预主密钥三者一起经过一轮推导得到最终的对称会话密钥。这样任何一方都无法单独决定密钥更安全。为什么不直接让客户端生成一个密钥用服务器公钥加密发过去老式 RSA 密钥交换就是这么做的。但它有个大问题没有前向保密。如果攻击者今天把你服务器的私钥偷走了他就能解密过去所有被录下来的流量因为当时的会话密钥是用那个私钥保护发出去的。现代 TLS 改用 ECDHE临时密钥交换每次会话临时生成密钥用完就扔——即使服务器私钥将来泄露也解不开过去的流量。这叫前向保密PFS。五、TLS 1.3 握手1-RTT更快也更安全TLS 1.3 做了两件大事更快正常握手只要1-RTT甚至支持 0-RTT。更安全砍掉所有已知有问题的老算法强制前向保密把更多握手内容也加密。TLS 1.3 之所以能 1-RTT是因为它假设双方会选 ECDHE于是客户端在第一个 ClientHello 里就把密钥交换参数key_share一起发过去了省掉了一个来回。而且从 ServerHello 之后的内容包括证书都是加密的中间人连你用的什么证书都看不到。我们来真实跑一次看服务器实际选了什么。在远端执行cd /opt/tls-api-labs openssl s_client -connect 127.0.0.1:443 -servername api.lab -CAfile ca/ca.crt -tls1_3 -brief /dev/null真实回显Connecting to 127.0.0.1 CONNECTION ESTABLISHED Protocol version: TLSv1.3 Ciphersuite: TLS_AES_256_GCM_SHA384 Peer certificate: CCN, OTLS-API Lab, CNapi.lab Hash used: SHA256 Signature type: rsa_pss_rsae_sha256 Verification: OK Negotiated TLS1.3 group: X25519MLKEM768 DONE命令逐参数解释openssl s_clientOpenSSL 自带的TLS 客户端工具用来手动建立一次 TLS 连接并打印握手细节。-connect 127.0.0.1:443连到哪个地址和端口地址:端口。-servername api.lab设置SNIServer Name Indication。SNI 是客户端在握手时告诉服务器我要访问哪个域名。一台服务器可能用同一个 IP 托管多个网站靠 SNI 决定出示哪张证书。没有它服务器可能给你错误的证书。-CAfile ca/ca.crt告诉客户端用这个 CA 证书来验证服务器。相当于把我们的自签 CA 临时信任一下。-tls1_3强制只用 TLS 1.3。对比用-tls1_2。-brief只打印一行行摘要不然会输出一大坨。/dev/null把标准输入接到空设备。因为s_client连上后会等你输入内容发给服务器我们只想看握手不想交互所以喂个空输入让它自己结束。怎么看回显Protocol version: TLSv1.3最终协商出来的版本。Ciphersuite: TLS_AES_256_GCM_SHA384加密套件。拆开看TLSAES_256_GCM用 AES-256 对称加密GCM 模式SHA384用 SHA-384 做完整性校验。Peer certificate: ... CNapi.lab服务器出示的证书。Verification: OK证书验证通过了因为我们-CAfile信任了它签发的 CA。Negotiated TLS1.3 group: X25519MLKEM768密钥交换用的算法组。这是一个后量子混合算法X25519 ML-KEM-768是较新 OpenSSL 的默认。这说明TLS 的算法一直在演进。再对比 TLS 1.2openssl s_client -connect 127.0.0.1:443 -servername api.lab -CAfile ca/ca.crt -tls1_2 -brief /dev/null真实回显Protocol version: TLSv1.2 Ciphersuite: ECDHE-RSA-AES256-GCM-SHA384 ... Peer Temp Key: X25519, 253 bitsECDHE-RSA-AES256-GCM-SHA384拆开看ECDHE临时椭圆曲线密钥交换提供前向保密RSA用 RSA 证书签名做身份认证AES256-GCM对称加密SHA384完整性。0-RTT 是什么TLS 1.3 还支持0-RTT如果客户端之前连过这个服务器它可以在第一个包里就带上加密的应用数据零个来回。代价是0-RTT 的数据可能被攻击者录制后重放因为不含新的随机数协商。所以只有幂等、安全的请求如 GET才适合 0-RTT涉及转账、下单这种绝不能重复的请求不要用。六、前向保密PFS定义即使服务器的长期私钥将来泄露了攻击者也无法解密过去录下来的历史流量。为什么重要攻击者常常先录流量、后偷密钥先把加密流量存着等哪天攻破服务器拿到私钥再解密。没有前向保密的话这一天到来时所有历史流量全暴露。怎么实现用 ECDHE每次会话临时生成一对密钥会话结束就丢弃。会话密钥不依赖长期私钥所以长期私钥泄露也没用。TLS 1.3 强制要求前向保密不再支持纯 RSA 密钥交换这是它比 1.2 更安全的重要原因之一。七、现实世界TLS 到底在哪里终止现实中TLS 不一定是你的后端应用在管。常见架构场景TLS 在哪终止说明小型网站nginx / Apache就是本实验的做法nginx 负责握手再把明文转给后端大厂负载均衡 / CDN用户的 TLS 在 CDN 边缘节点终止CDN 到源站可能再套一层 TLS云服务云负载均衡 / API 网关证书上传到云平台平台统一管理微服务内部服务网格mTLS服务之间互相也要证书双向认证TLS 终止TLS termination的意思是加密在这里被解开之后到后端就是明文了。所以公网那段是加密的用户 ↔ nginx/CDN。内网那段可能又是明文nginx ↔ 后端也可能再加密。本实验就是nginx 终止 TLS后端127.0.0.1:9443是明文——这是最常见的部署方式也方便你在 nginx 后面看明文。ALPN握手时客户端还会告诉服务器我支持 HTTP/2 还是 HTTP/1.1这叫 ALPN应用层协议协商。用 curl-v能看到* ALPN: curl offers h2,http/1.1 * ALPN: server accepted http/1.1八、本篇小结HTTP 是明文抓包直接看到账号密码TLS 给它套上加密管道。TLS 同时解决机密性、完整性、身份认证三件事。五种密码学工具对称加密快但密钥分发难、非对称加密解决分发但慢、哈希指纹、HMAC带密钥的指纹、数字签名私钥签公钥验。TLS 的组合拳用非对称安全地协商出一把对称密钥之后用对称加密传数据。TLS 1.2 是 2-RTTTLS 1.3 是 1-RTT砍掉弱算法、强制前向保密、加密更多握手内容。前向保密长期私钥泄露也解不开历史流量。现实里 TLS 常在 nginx/CDN/负载均衡处终止之后到后端可能是明文。九、总结TLS 要解决哪三个问题各用什么手段答机密性对称加密、完整性HMAC/签名、身份认证数字证书。三者必须同时满足少一个都会被攻击者利用。对称加密和非对称加密各自的优缺点是什么TLS 为什么两个都用答对称加密快适合加密大量数据但密钥分发难怎么安全地把同一把钥匙给对方非对称加密解决了分发公钥可公开但慢。TLS 用非对称安全地协商出一把对称密钥之后所有数据用对称加密——兼得安全与速度。哈希和 HMAC 的区别是什么为什么哈希本身不能防篡改答哈希不需要密钥任何人都会算只能验证数据有没有变HMAC 是哈希 共享密钥还能证明是拿着密钥的人发的。因为哈希谁都能算攻击者改了数据后可以自己重算一个哈希替换掉接收方照样验得过所以哈希单独不能防篡改。TLS 1.3 为什么能比 1.2 少一个 RTT它牺牲了什么换来了 0-RTT答1.3假设双方会用 ECDHE客户端在第一个 ClientHello 里就把密钥交换参数key_share一起发过去省掉一个来回所以只要 1-RTT。0-RTT 是客户端在第一个包里就带应用数据代价是这些数据缺少新的随机数协商可能被攻击者录制后重放——所以只适合幂等、安全的请求如 GET。前向保密是什么意思没有它会发生什么答前向保密PFS即使服务器的长期私钥将来泄露也解不开过去录下来的历史流量。实现方式是 ECDHE每次会话临时生成密钥用完即弃。没有它攻击者可以先录流量、后偷私钥等拿到私钥那天把历史流量全部解密。SNI 是干什么用的没有它可能出现什么问题答SNI 让客户端在握手时告诉服务器我要访问哪个域名。一台服务器一个 IP可能托管多个网站靠 SNI 决定出示哪张证书。没有它服务器可能返回默认站点/错误的证书导致证书校验失败或访问到错误的网站。
返回列表