ARTICLE DETAIL

资讯详情

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

TLS + Web API 安全 · 03 · TLS 实战:证书、漏洞与抓包中间人

TLS + Web API 安全 · 03 · TLS 实战:证书、漏洞与抓包中间人 本文为教学用途所有复现均在作者自建靶场内完成。请勿在未授权的情况下对真实系统进行测试。根据《网络安全法》未经授权测试、攻击他人系统属于违法犯罪行为。禁止复制本文Payload 对外部站点进行测试违规使用造成的一切后果由使用者自行负责一、启动实验靶场靶场已经写好一键启动bash /opt/tls-api-labs/up.sh它依次做了六件事每一步的脚本都在ca/和app/下可以打开看生成自签根 CAca/gen-ca.sh用根 CA 签发服务器证书SAN 包含api.lab / localhost / 127.0.0.1 / 192.168.143.156ca/gen-server-cert.sh生成一张不受信任的 rogue 证书供中间人演示ca/gen-rogue-cert.sh生成客户端证书供双向 TLS 演示ca/gen-client-cert.sh生成 JWT 用的 RSA 密钥对app/gen-jwt-keys.sh启动后端 APIapp/vuln_api.py监听127.0.0.1:9443并把 nginx 配置铺上去、重载架构是这样的物理机浏览器 / curl │ ├── http://api.lab:8081 ──┐明文 │ │ └── https://api.lab:443 ──┤ nginx ── 127.0.0.1:9443 (vuln_api.py明文) │ 中间人演示 https://127.0.0.1:8443mitm_demo.py也转发到这里关键点TLS 在 nginx 这里终止nginx 到后端是明文。这是现实中最常见的部署方式。up.sh用systemd-run起后端服务名字叫tls-api。查看状态/日志systemctl status tls-api journalctl -u tls-api -n 50 --no-pager改了后端代码后要systemctl restart tls-api才生效。二、用 openssl 手动走一遍握手openssl s_client是一个手工 TLS 客户端能让我们把握手过程看个通透。2.1 看 TLS 1.3 握手摘要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参数回顾详见第 01 篇-connect连哪、-servername设置 SNI、-CAfile信任哪个 CA、-tls1_3强制版本、-brief精简输出、/dev/null不进入交互。重点看Verification: OK因为我们在-CAfile里信任了自签根 CA所以验证通过。如果去掉-CAfileopenssl s_client -connect 127.0.0.1:443 -servername api.lab -brief /dev/null 21 | grep -i verify会看到真实回显意思是找不到签发者的证书、无法验证第一张证书——因为客户端不认识我们那个自签 CA。2.2 强制旧版本看客户端和服务器谁在拒绝openssl s_client -connect 127.0.0.1:443 -tls1_1 /dev/null 21 | head -5真实回显注意这次失败其实是客户端自己拒绝的不是服务器。新版 OpenSSL3.x在库里默认就把 TLS 1.0/1.1 关掉了所以客户端根本不愿意发起 1.1 的握手报no protocols available。要真正验证服务器是否接受 1.1得先把客户端的最低协议降下来再发一次cat /tmp/ossl_old.cnf EOF openssl_conf openssl_init [openssl_init] ssl_conf ssl_sect [ssl_sect] system_default system_default_sect [system_default_sect] MinProtocol None CipherString DEFAULTSECLEVEL0 EOF OPENSSL_CONF/tmp/ossl_old.cnf openssl s_client -connect 127.0.0.1:443 -tls1_1 /dev/null 21 | head -5真实回显这次客户端确实把 TLS 1.1 的 ClientHello 发出去了服务器回了一个protocol version告警SSL alert 70明确拒绝。这才证明是nginx 的ssl_protocols配置在服务端拦住了旧版本。MinProtocol None让客户端不再自我限制最低版本。CipherString DEFAULTSECLEVEL0把安全等级降到 0否则 1.1 可能因为算法太弱而被客户端拒绝。OPENSSL_CONF...临时用这份配置启动 openssl不修改系统配置。所以旧版本是两边都在拒绝客户端默认不用服务器也明确拒绝——这就是纵深防御。禁用 TLS 1.0/1.1 是现代安全基线因为它们是几十年前的设计存在 BEAST、POODLE 等已知漏洞见第七节。为什么回显里还有CONNECTED那是 TCP 层连上了TCP 三次握手成功但 TLS 层协商失败。no peer certificate available说明 TLS 握手没完成、没拿到证书。2.3 看完整证书链openssl s_client -connect 127.0.0.1:443 -servername api.lab -CAfile ca/ca.crt -showcerts /dev/null 2/dev/null | sed -n /Certificate chain/,/Server certificate/p真实回显节选Certificate chain 0 s:CCN, OTLS-API Lab, CNapi.lab i:CCN, OTLS-API Lab, CNTLS-API Lab Root CA a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption v:NotBefore: Sep 22 15:31:41 2026 GMT; NotAfter: Dec 25 15:31:41 2028 GMT这就是第 02 篇讲的信任链。三、协议版本与加密套件3.1 版本版本状态说明SSL 2.0 / 3.0已废弃有严重漏洞DROWN、POODLE任何现代配置都应禁用TLS 1.0 / 1.1已废弃2020 年前后各大浏览器陆续停止支持有 BEAST 等漏洞TLS 1.2仍在使用支持 AEAD 套件、前向保密配置正确时是安全的TLS 1.3推荐1-RTT、强制前向保密、加密更多握手内容3.2 加密套件Cipher Suite套件名字看着长其实有固定结构。以ECDHE-RSA-AES256-GCM-SHA384为例ECDHE 密钥交换算法临时椭圆曲线提供前向保密 RSA 身份认证算法用 RSA 证书签名 AES256 对称加密算法256 位 AES GCM 对称加密的工作模式AEAD自带完整性 SHA384 完整性校验用的哈希TLS 1.3 把密钥交换和认证分开协商套件名简化成TLS_AES_256_GCM_SHA384这种只剩对称加密 哈希。安全要点优先选带ECDHE的套件有前向保密。对称加密选AES-GCM或ChaCha20-Poly1305都是 AEAD加密和完整性一体比 CBC 模式安全。避开 CBC 模式POODLE、Lucky13、RC4已破、3DES太弱。避开EXPORT、NULL、anon匿名不认证套件。四、curl 的证书校验行为curl是最常用的命令行 HTTP 工具它默认严格校验证书。4.1 不信任 CA → 失败curl -s https://127.0.0.1/api/health ; echo exit$?退出码 60 证书验证失败自签、过期、域名不符都会是它。4.2 信任 CA → 成功curl -s --cacert ca/ca.crt https://127.0.0.1/api/health ; echo exit$?--cacert ca/ca.crt指定用这个文件作为信任的 CA相当于把我们的自签 CA 加进信任列表。4.3-k跳过校验 → 成功但危险curl -sk https://127.0.0.1/api/health ; echo exit$?-k/--insecure跳过证书验证。连上了但对面是谁完全没保证——这正是中间人攻击的温床。生产脚本里出现-k是危险信号。4.4 主机名不匹配 → 失败curl -s --cacert ca/ca.crt --resolve api2.lab:443:127.0.0.1 https://api2.lab/api/health ; echo exit$?证书 SAN 里没有api2.lab主机名校验失败。4.5 用 curl 看握手细节curl -sv --cacert ca/ca.crt https://127.0.0.1/api/health 21 | grep -E SSL connection|subject:|issuer:|TLSv|ALPN|HTTP/真实回显这就是第 01 篇画的握手图在真实工具里的样子。-v是 verbose详细模式21把错误输出curl 的调试信息走 stderr合并到标准输出才能被 grep 到。五、抓包对比明文 vs 加密这是理解 TLS 价值最直观的实验。5.1 抓 HTTP 明文8081cd /opt/tls-api-labs (timeout 6 tcpdump -i lo -A -s0 port 8081 /tmp/plain.txt 2/dev/null ) 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真实回显账号密码明文可见。5.2 抓 HTTPS 流量443(timeout 6 tcpdump -i lo -s0 -w /tmp/https.pcap port 443 /dev/null 21 ) sleep 1 curl -s -k https://127.0.0.1/api/login \ -H Content-Type: application/json \ -d {username:alice,password:password1} /dev/null sleep 5 ​ echo 搜索明文 password 的次数 tcpdump -r /tmp/https.pcap -A 2/dev/null | grep -ac password || echo 0 echo 抓到的包前几行 tcpdump -r /tmp/https.pcap 2/dev/null | head -6真实回显搜索password次数为 0——加密之后抓包工具只能看到 TCP 层的信息源端口、标志位、长度看不到内容。-w /tmp/https.pcap是把原始包写成 pcap 文件可以用 Wireshark 打开-r是读取 pcap 文件回放。既然抓包看不到内容攻击者是不是就没办法了不一定。攻击者还有两条路一是拿到服务器私钥没有前向保密就能解历史流量二是中间人——在握手阶段插进去。六、中间人攻击MITM演示6.1 原理中间人攻击的核心是攻击者插在客户端和服务器中间对客户端冒充服务器对服务器冒充客户端。客户端 ──TLS── 攻击者 ──TLS── 真服务器 看到明文 重新加密攻击者要成功必须让客户端相信攻击者的证书就是真服务器的证书。如果他拿不到受信任的证书只能用自签证书。这时候客户端正确校验证书→ 发现不受信任 → 拒绝连接攻击失败。客户端不校验证书点了继续访问 / 用了-k/ App 里写死了忽略证书校验→ 连接成功攻击者拿到明文。6.2 动手起一个中间人我们的app/mitm_demo.py用那张 rogue 证书在127.0.0.1:8443提供 TLS解密后再转发给真后端并把明文打印出来。cd /opt/tls-api-labs (timeout 12 python3 app/mitm_demo.py /tmp/mitm.log 21 ) sleep 1.5 ​ echo ### 客户端【校验证书】访问 MITM 端口 curl -s --cacert ca/ca.crt https://127.0.0.1:8443/api/health 21 ; echo exit$? ​ echo ### 客户端【不校验证书】访问 MITM 端口 curl -s -k -X POST https://127.0.0.1:8443/api/login \ -H Content-Type: application/json \ -d {username:alice,password:password1} ; echo ​ sleep 1 echo ### 中间人截获到的明文 head -20 /tmp/mitm.log真实回显结论校验证书时中间人直接被拒exit60。不校验时中间人拿到了完整明文包括账号密码。这就是为什么不能忽略证书警告的最好证明。6.3 防御中间人防御说明正确校验证书客户端/App 不要关掉校验不要盲目-k、不要信任所有证书用受信任 CA 签发的证书用户不会被警告训练成点继续HSTS强制浏览器以后只用 HTTPS防止降级到 HTTP 再被劫持证书固定Certificate PinningApp 里写死只信任某张证书/某个公钥即使系统信任了假 CA 也没用关注证书告警任何证书异常都应停下排查而不是继续访问七、TLS 常见漏洞史漏洞年份影响原理一句话Heartbleed2014OpenSSL 1.0.1心跳扩展的越界读攻击者能读到服务器内存可能含私钥一个请求就能泄露数据POODLE2014SSL 3.0CBC 填充 oracle把 SSL 3.0 降级后逐字节解密BEAST2011TLS 1.0 CBCIV 可预测逐字节解密DROWN2016SSLv2用支持 SSLv2 的服务器去解密 TLS 流量降级攻击——版本协商攻击者篡改握手逼双方用最弱的版本/算法防御主线禁用 SSLv2/3 和 TLS 1.0/1.1只用 TLS 1.2/1.3 强套件开启 HSTS及时升级 OpenSSL正确校验证书。本实验的 OpenSSL 3.5.5 早已修复 Heartbleed默认也禁用了旧协议。八、物理机浏览器实操导入自签 CA前面都是用 curl 在命令行里看证书。这一节我们把自签 CA 装进物理机浏览器用眼睛看证书告警 → 受信任的变化。前提物理机与远端同网段且已在 hosts 里加了192.168.143.156 api.lab8.1 先把 CA 下载到物理机在物理机上执行sshpass -p 123 scp root192.168.143.156:/opt/tls-api-labs/ca/ca.crt ./ca.crtscp是远程拷贝把远端文件下到本地后面两个参数是远端路径和本地目标。注意ca.key私钥绝对不能下载或外传只需要ca.crt证书。8.2 导入前的样子反面教材浏览器打开https://api.lab。因为浏览器不认识这个自签 CA会拦截Chrome/Edge您的连接不是私密连接/NET::ERR_CERT_AUTHORITY_INVALID需要点高级 → 继续前往才能进。点继续前往后地址栏显示不安全红色/带感叹号不是锁。攻击者用自签证书做中间人时用户看到的就是它。点继续等于放弃身份认证。8.3 导入 CA 到受信任的根证书颁发机构导入的原理把我们的ca.crt加进客户端的信任锚列表之后它签发的证书就能一路验通。Windows双击ca.crt→ 安装证书 → 存储位置选本地计算机 → 将所有的证书都放入下列存储 →受信任的根证书颁发机构→ 完成。之后可能需要重启浏览器。macOS双击ca.crt导入钥匙串访问 → 找到该证书 → 右键显示简介 → 信任 → 把使用此证书时设为始终信任。Linux系统级Debian/Ubuntusudo cp ca.crt /usr/local/share/ca-certificates/tls-api-lab.crt sudo update-ca-certificates第一条把证书放进系统目录第二条重建系统信任库。Firefox它用自己独立的信任库不跟随系统设置 → 隐私与安全 → 证书 → 查看证书 → 证书颁发机构 → 导入 → 勾选信任由此证书颁发机构来标识网站。为什么 Firefox 要单独导入因为 Firefox 不读操作系统的信任库而是维护自己的 NSS 证书库。Chrome/Edge 在 Windows/macOS 上跟随系统在 Linux 上则可能用 NSS。所以导入后 Chrome 好了、Firefox 还报警是正常现象。8.4 导入后的样子正常网站再打开https://api.lab地址栏出现锁形图标不再有警告。点锁 → 连接是安全的 → 证书有效 → 可以查看证书链TLS-API Lab Root CA→api.lab。有效期、SANapi.lab、localhost、127.0.0.1、192.168.143.156。公钥算法、签名算法。8.5 用 DevTools 看 API 请求按F12或右键 → 检查打开开发者工具切到Network网络面板刷新页面或访问https://api.lab/api/healthHeaders能看到请求方法、URL、状态码以及Authorization: Bearer ...这类请求头。Response能看到返回的 JSON。Application/存储很多前端会把 token 放在localStorage在这里能直接看到这也是它容易被 XSS 偷走的原因。8.6 实验做完把 CA 删掉自签 CA 只应存在于实验环境。做完实验建议移除Windowscertmgr.msc→ 受信任的根证书颁发机构 → 找到并删除。macOS钥匙串访问里删除。Linuxsudo rm /usr/local/share/ca-certificates/tls-api-lab.crt sudo update-ca-certificates --fresh。九、现实世界生产环境怎么配 TLS配置项生产建议为什么协议版本只开 TLS 1.2 / 1.3旧版本有已知漏洞套件只留 AEAD ECDHE前向保密 完整性证书Lets Encrypt / 商业 CA自动续期避免过期事故HSTS开启max-age至少半年防降级、防 Cookie 劫持OCSP Stapling开启提速、保护隐私私钥权限600仅服务进程可读私钥泄露等于身份被冒用证书监控到期前告警过期 全站挂重定向80 端口 301 到 443避免用户走明文关于 HSTS 的坑HSTS 一旦被浏览器记住在max-age内就算证书出问题用户也没法点继续访问——这是好事安全但调试自签证书时很麻烦。所以本实验默认没开 HSTS避免把你的浏览器锁死。生产环境则应该开。十、本篇小结靶场up.sh一键起自签 CA 服务器证书 nginx(443) 后端 API(9443) 明文对比(8081)。openssl s_client能手动走握手、看版本/套件/证书链默认情况下 OpenSSL 自己就拒绝 TLS1.1客户端限制把客户端最低协议降下来后服务器会回protocol version告警服务端限制。curl 默认严格校验不信任 CA → 退出码 60-k能连但危险主机名不符也报 60。tcpdump 抓包HTTP 明文可见HTTPS 搜不到明文。中间人客户端校验 → 被拒不校验 → 明文全泄露。防御主线禁用旧版本、强套件、正确校验证书、HSTS、证书固定。十一、总结openssl s_client的-servername是干什么的不加会怎样答它设置SNI告诉服务器我要访问哪个域名服务器据此决定出示哪张证书。不加的话服务器可能返回默认站点的证书域名对不上导致校验失败或者把你导向错误的网站。curl 退出码 60 代表什么有哪几种原因会导致它答代表证书验证失败。原因包括自签/不受信任的 CA、证书过期、访问域名与证书 SAN 不符、缺少中间证书、客户端系统时间不对、证书被吊销等。为什么抓 HTTPS 的包看不到内容中间人又是怎么看到明文的答HTTPS 的应用数据被对称加密抓包只能看到 TCP 头和加密的 TLS 记录看不到明文。中间人不是在解密而是在握手阶段插进中间对客户端用一张不受信任的证书冒充服务器客户端若不校验就会和中间人建立加密连接、把明文交给它中间人再重新加密转发给真服务器。中间人攻击成功的前提是什么怎么防答前提是客户端没有正确校验证书点了继续访问、用了-k、App 里写死忽略校验或者客户端系统被安装了攻击者的根 CA。防御正确校验证书、用受信任 CA 签发的证书、开启 HSTS、App 做证书固定pinning。ssl_protocols TLSv1.2 TLSv1.3;这行配置为什么重要答它让nginx 只接受 TLS 1.2/1.3在服务端拦住了存在已知漏洞BEAST、POODLE、DROWN 等的 SSL 3.0 和 TLS 1.0/1.1缩小了攻击面。允许旧版本等于给攻击者留了降级的口子。HSTS 有什么用为什么调试自签证书时不建议开答HSTS 让浏览器在有效期内强制只用 HTTPS访问该站防止降级到 HTTP 被劫持。调试自签证书时不建议开是因为 HSTS 一旦被浏览器记住在max-age内就算证书有问题也不允许继续访问会把你锁在打不开的状态很麻烦。
返回列表