S20

TLS 与 HTTPS

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 为什么在 TCP 之后还需要 TLS

在 S19 中,TCP Connected 只表示传输连接已经建立,不能据此宣布 HTTP 请求成功。本课继续把连接拆开:TCP 负责传送字节,TLS 在其上建立受保护的通道,HTTP 再通过通道表达请求与响应。讨论范围是常见的 HTTP/1.1、HTTP/2 经 TCP 与 TLS 传输的路径;其他传输组合不在这里展开。

设想你在咖啡馆 Wi-Fi 中访问账户页面。设备能连到某个地址的 443 端口,只能说明这条网络路径上有服务接受连接。假如地址解析、无线网络或中途转发受到操纵,你可能正在向冒充站点的服务发送字节。即使流量确实经过目标服务器,缺少保护的内容也可能在途中被读取或修改。TCP 的可靠、有序传输不会替你判断对方是不是预期的网站。

TLS 主要补上 3 类能力。保密性让旁观者难以直接读出应用数据;完整性让传输中的篡改能够被发现;身份核对让客户端根据证书、名字和信任规则判断连接对象。在常见公网 HTTPS 中,客户端主要核对服务器身份。本课不展开客户端证书认证,也不需要先背诵密码算法目录。

这些能力要按实际配置成立。客户端关闭证书校验后,通道即使仍加密,也失去了正常的服务器身份核对。反过来,证书有效也不能证明页面没有恶意脚本、接口没有越权漏洞,或者登录后的数据访问一定符合权限。TLS 保护通信通道;网站的业务逻辑与服务器本身仍需要各自的安全措施。

因此,交付结论要写清楚边界。“TCP 已连接”“TLS 身份核对通过”“HTTP 返回预期内容”是逐步获得的证据。后面的阶段失败时,不能把前面阶段的成功扩大成整体成功,也不能把所有错误都归到网络不通。

TCP、TLS 与 HTTP 的交付边界
TCP、TLS 与 HTTP 的交付边界

2 证书与名字:站点身份如何被核对

客户端访问 https://api.example/health 时,要核对的名字来自 URL:api.example。站点证书通过主题备用名称 SAN 声明适用的域名或 IP 地址。现代客户端主要依据 SAN 做名称匹配,不能只看证书展示界面中的组织名称,也不能只凭页面能打开就认定身份正确。

假设某张证书的 SAN 包含 api.example 和 www.example。访问前者可以进行对应的域名核对,但这不自动覆盖 admin.example。通配符也有匹配范围:*.example.com 通常覆盖 api.example.com,不覆盖根域 example.com,也不覆盖 a.api.example.com。交付前应列出实际使用的名字,再与证书声明逐项对照。

直接把 URL 改成 IP 是常见陷阱。https://192.0.2.10/health 要核对的是这个 IP;证书只包含 api.example 时,解析到同样的机器也不能通过名称校验。证书若要用于 IP URL,需要包含相应的 IP 地址 SAN。名字表示预期身份,IP 表示连接目标,两者有关联,却不能相互替代。

同样的 IP 与端口还可能承载多个 HTTPS 站点。客户端通常在 TLS 握手中通过 SNI 告诉入口自己要访问哪个服务器名字,入口据此选择证书。证书尚未选对时,HTTP 请求还没有开始。SNI 是握手中的选择信息,本身不等于身份已经可信,最终仍要检查返回证书是否匹配 URL、链是否可信以及时间是否有效。

这里要与 S19 的 HTTP Host 区分。SNI 用于 TLS 阶段选择站点和证书;HTTP/1.1 的 Host 用于 HTTP 阶段选择资源所属站点。只给请求添加 Host: api.example,不能修复以 IP URL 发起连接造成的证书名称错误,因为身份核对发生得更早。

测试指定入口时,可以保留 URL 中的域名,并用 curl --resolve api.example:443:192.0.2.10 https://api.example/health 临时指定连接地址。这样连接到指定 IP,同时仍按 api.example 发送 SNI 并核对身份。示例域名与地址用于教学,执行时应替换为自己的授权测试目标。

URL、SNI、证书 SAN 与 HTTP Host
URL、SNI、证书 SAN 与 HTTP Host

3 信任链:谁签发、客户端信谁

域名匹配解决“证书声明的名字是否正确”,但声明还需要可信的签发依据。常见关系是:终端实体证书由中间 CA 签发,中间 CA 再衔接到根 CA。服务器使用终端实体证书及对应私钥证明身份;客户端沿证书链验证签发关系,最终落到自己信任的根。

根证书通常来自操作系统、浏览器或应用维护的信任库。信任根是客户端的本地决定,不能因为服务器把某张根证书发送过来,客户端就自动信任它。不同客户端的信任库和更新状态可能不同,因此“我的浏览器能打开”不能覆盖所有调用方的验收。

设服务端需要提供 1 张终端实体证书和 1 张中间证书,却只部署了终端实体证书。某台浏览器可能因缓存已有中间证书而成功,另一个容器里的 curl 却无法构建完整链。浏览器成功与容器失败并不矛盾,它们掌握的链材料可能不同。服务端应提供所需的中间链,通常无须发送根证书,不能把偶然缓存当成部署条件。

信任链也不是“证书文件越多越好”。误放无关中间证书、沿用错误的链文件、轮换后没有同步入口配置,都可能造成客户端找不到通往可信根的路径。排查时应记录服务器实际送出的链,以及失败客户端使用的信任库,不能只检查磁盘上准备部署的文件。

自签证书在本课只用于隔离实验:参与者明确知道谁签发,如何分发信任,以及实验何时结束。把实验中的“点继续”或跳过校验习惯直接搬到公网,会使访问者无法按常规公共信任体系核对身份。组织内部若使用私有 CA,也需要受控分发信任与维护流程,不能把“内网”当作天然可信的理由。

验收时至少分别回答名称、时间、信任链这 3 个问题。名称正确却链不完整,或者链可信却证书已过期,都不构成有效的 HTTPS 身份核对。将它们分开记录,才能解释客户端为什么拒绝连接。

从站点证书验证到客户端信任根
从站点证书验证到客户端信任根

4 握手直觉:协商出保密通道(不展开算法细节)

把握手理解为建立通道前的协商:客户端说明自己支持的协议能力及目标名字,服务器选择兼容的参数并提供身份材料,双方按协议完成身份验证、密钥建立和握手确认。具体消息顺序随 TLS 版本、恢复方式等条件变化;这里用完整握手的教学时序理解阶段,不把示意图当成逐包抓取结果。

先看 1 次假想请求。第 0 毫秒开始连接;第 25 毫秒 TCP 建立;第 70 毫秒 TLS 握手完成,客户端已按正常规则接受证书;第 95 毫秒收到 HTTP 响应。这些数字只用于区分阶段,不代表固定性能指标。连接耗时不是完整接口耗时,接口耗时也不能只由握手速度推断。

如果在第 45 毫秒发现名称不匹配,客户端可能中止 TLS,根本不会发送正常的 HTTP 业务请求。此时服务端应用访问日志里没有该请求,并不能证明客户端从未连接过入口。反之,TLS 完成后返回 HTTP 503,说明请求已经越过了握手阶段,应继续检查网关转发、上游服务和业务状态。

握手之后,HTTP 请求与响应作为应用数据受到 TLS 保护。HTTP 字段与内容不再像明文传输那样直接暴露给普通链路旁观者,不过通信的目标地址、流量大小和时间等信息仍可能被观察。不要把“应用数据加密”理解成所有网络现象都被隐藏。

验收还要记录实际协商出的 TLS 版本。例如团队策略允许 TLS 1.2 与 TLS 1.3,观察到 TLS 1.3 可以满足这一项协议要求,但仍须检查证书与业务探针。反过来,看到 TLS 1.2 也不能直接判失败,要与既定策略比较。策略应兼顾安全要求和真实客户端能力,不在故障中临时凭印象决定。

日志里的协议名称提供的是阶段证据。出现握手完成信息不等于业务返回 200;出现 200 也要确认它来自正常证书校验下的连接,并且响应内容符合预期。把这些条件写成独立验收项,比笼统记录“HTTPS 通了”更容易复查。

教学时序中的 TCP、TLS 与 HTTP
教学时序中的 TCP、TLS 与 HTTP

5 HTTPS:在 TLS 保护下传输 HTTP

HTTPS 可以理解为通过受 TLS 保护的通道传输 HTTP。URL 中的 https 指定这类访问方式;未显式指定端口时,默认使用 443。http 默认使用 80。端口号是约定与配置,服务也可以在其他端口提供 HTTPS,所以判断时要看 URL、端口与监听服务的实际类型是否一致。

很多站点让 80 端口返回重定向,引导客户端访问 HTTPS。这能改善正常访问路径,但首次 HTTP 请求仍是明文,重定向本身不能独立证明站点身份。客户端仍须对跳转后的 HTTPS 连接进行完整校验。涉及凭据和敏感内容的调用,应直接使用确认过的 HTTPS URL,避免先把数据提交到明文入口。

HSTS 的直觉是:浏览器在安全接收相关策略后,会在策略有效范围内坚持使用 HTTPS;首次访问也可能通过预加载覆盖,但并非所有站点和客户端都有这一条件。

TLS 常在负载均衡器或反向代理处终止。客户端看到的证书属于该入口,入口到后端则是另一段连接,可能使用 HTTP,也可能再次使用 TLS。因此“外部已经 HTTPS”不能自动证明内部各段都受加密保护。方案中应标出终止位置,并说明后端链路如何满足自己的安全要求。

若入口到后端也使用 TLS,入口此时就是 TLS 客户端,需要独立核对后端身份。例如后端证书只覆盖 origin.example,入口连接后端 IP 时,应配置预期名称、对应 SNI 和受管理的信任库;仅把回源端口改为 443,不代表已经开启这些校验。若后端误换成其他站点的证书,正确配置的入口应拒绝连接,外部用户可能收到入口生成的 502。此时浏览器与入口之间的 TLS 仍可正常完成,失败的是入口与后端之间的握手。排查要同时查看入口的回源错误和后端实际证书,不能仅凭浏览器中的有效证书排除 TLS 故障。

HTTP 业务验收也要明确语义。/health 返回 200 可以证明该探针可用,却未必覆盖鉴权、数据库写入和关键交易。若健康页由入口直接生成,即使后端失效也可能返回正常。选择探针时要说明它经过哪些组件,结论只覆盖实际走过的路径。

6 证书生命周期:申请、部署、轮换与监控

证书会过期,部署也会变化。完整生命周期包括确定域名和责任人、按签发要求证明控制权、申请证书、保管私钥、部署证书与中间链、验证生效、监控剩余有效期以及提前轮换。申请成功只说明拿到了材料;外部客户端看到的新证书已经生效,才说明部署步骤完成。

证书的有效区间由 notBefore 与 notAfter 等字段表达。客户端需要合理准确的时间,才能判断当前是否位于区间内。监控应面向线上实际提供的证书,不能只盯申请平台或证书文件目录。新文件已经下载,但入口进程没有重载,访问者仍可能收到旧证书。

看 1 个数值案例:今天证书还剩 12 天,监控只在剩余 1 天时告警,而组织完成审批、发布、入口验证和必要回退预留共需 3 天。假设期间没有主动续期,告警到达时最多只剩约 24 小时,已经无法从容走完 3 天窗口。如果监控每天检查 1 次,触发后还可能再延迟接近 1 天才被发现。

于是运维人员可能觉得证书“突然”全站报错,实际风险已经积累了 12 天。若大量入口共用同样的到期时间,它们会在接近同样的时刻失效。以这个教学条件看,阈值设为 7 天,比 1 天更可能容纳 1 天检测延迟、3 天轮换窗口和额外余量;真实阈值要按实际流程、假期和证书期限制定。

自动续期需要同时监控任务与结果。任务执行成功,却没有把新证书装到正确入口;部署命令返回成功,却遗漏某个区域;配置已经更新,进程却拒绝重载,这些都可能让线上证书继续倒计时。应从外部复测有效期,并抽查各入口,确认续期真的改变了服务状态。

私钥与证书的公开部分不同。证书可以提供给客户端,私钥必须限制访问和复制,避免进入代码仓库、聊天记录或公开日志。发现私钥泄露时,仅延长原证书有效期不能解决问题,应按事件流程更换密钥和证书,并考虑吊销等处置。备份也要有访问控制,不能因便于恢复而扩大泄露面。

多节点不一定必须使用相同的证书文件,但每个可达入口都必须呈现匹配域名、有效且受信任的身份材料。轮换采用分批发布时,要记录节点与证书指纹、有效期的对应关系。旧新证书短暂并存可以是正常过程,遗漏节点导致旧证书过期则属于交付缺口。

证书轮换从申请到线上验证
证书轮换从申请到线上验证

7 常见坑:用 -k 验收、改时钟消过期、明文与 TLS 端口混用

curl -k 或 --insecure 会跳过正常的服务器证书校验。它有时可在受控排查中帮助判断“忽略身份错误后是否还能到达 HTTP”,但得到 200 不能当作 HTTPS 验收通过。应先保留正常校验失败的原始证据;若确有诊断需要使用它,结果必须标注为跳过校验,修复后重新以正常校验验证。

浏览器点击“继续访问”具有同样的问题:这是一种绕过,不是问题已经消失。验收记录应使用不需要例外操作的客户端,并说明所用信任环境。曾经手工导入过实验根证书的个人电脑,不能单独代表公网普通用户的信任条件。

把客户端时钟往回调,可能暂时躲过过期判断,却会扭曲其他证书、登录令牌、定时任务和日志时间。正确做法是先确认真实时间:若客户端确实走快,修复时间同步;若当前时间正确而证书已到期,就更换线上证书。不能以改错时间的方式让错误材料看似有效。

明文与 TLS 服务类型反了,也会产生看似神秘的错误。向只接受 HTTP 的端口发送 TLS 握手,可能出现协议版本或握手相关错误;向只接受 TLS 的端口发送明文 HTTP,可能被断开或收到提示。具体错误文字取决于实现,不能看到某个关键字就断言证书损坏。先核对 URL 的 scheme、端口、负载均衡转发方式和后端监听协议。

还有一种搬运错误:隔离实验使用自签证书、人工导入信任或跳过校验,迁移到公网时却只改了域名和机器地址。公网调用方不会自动拥有实验环境的信任配置。上线方案应从目标用户的实际客户端出发,重新确认公共信任链及部署方式。

这些坑有共同特点:把暂时取得响应当作身份核对已经通过。合格证据应能被其他同事在既定环境中复现,而不依赖隐含的浏览器例外、错误时间或个人机器特有的信任配置。

8 案例:对外 API 的 HTTPS 上线验收

团队计划上线 https://api.example/health,入口有 2 个地址,后面连接 API 服务。交付要求是普通目标客户端能正常核对身份,协议符合团队允许 TLS 1.2、TLS 1.3 的策略,业务探针返回约定状态和内容。验收从清晰的目标开始,避免用“浏览器能打开”代替接口交付标准。

先确认入口与名字。清单中写明公开 URL、实际连接端口、TLS 终止位置,以及 2 个入口地址。证书 SAN 必须覆盖 api.example,有效期覆盖当前时间,服务端应提供所需中间链。根证书是否受信任,要以目标客户端环境为准,不能临时导入任意根证书后宣布公网验收通过。

正常请求可以用以下形式保留响应头与正文,实际记录中注意删去凭据与敏感内容:

curl -v --connect-timeout 5 --max-time 15 https://api.example/health

这里的 5 秒和 15 秒是本次教学验收选择的超时边界,不是 TLS 标准要求。详细输出可以帮助观察连接阶段、证书核对和协商信息;不同 curl 版本、TLS 后端的文字会有区别。curl 默认情况下即使收到 HTTP 500 也可能正常退出,因此还要读取 HTTP 状态和响应内容,不能只依据退出码判断业务成功。

接着分别指定入口地址,同时保留 URL 名字。例如对第 1 个入口使用:

curl -v --resolve api.example:443:192.0.2.10 https://api.example/health

对第 2 个入口替换为 192.0.2.11,重复正常校验。记录请求时间、连接地址、证书 SAN、到期时间、链校验结论、协商协议版本、HTTP 状态和探针结果。若入口还有其他区域或 IPv6 地址,也应按实际发布范围覆盖,不能从 2 个样本推断未知入口全部正常。

假设第 1 个入口证书与链均正常,返回 200 和预期的 status=ok;第 2 个入口名称匹配,却因缺少中间证书在目标容器中失败。此时上线结论是尚未通过。第 1 个入口的成功不能覆盖第 2 个入口,给失败请求补 -k 后得到 200 也不能改变链校验失败这一事实。

修复第 2 个入口的链配置、重载服务后,再以同样环境与正常校验复测。若 TLS 已通过但返回 503,则证书修复已产生效果,剩余问题进入 HTTP 和上游服务阶段。交付报告可以分别记录这 2 个结论,避免因业务仍异常而误判证书修改没有生效。

最后核对续期责任、监控对象与阈值、轮换窗口、失败通知去向和私钥存放方式。技术探针通过解决的是当下可用;这些交接信息决定服务能否在下次到期前继续可用。验收材料应保留正常校验结果与失败复测记录,而不是只留最终成功截图。

9 排查案例:浏览器报不安全 / curl 证书错误 / 偶发握手失败

某 API 上线后收到 3 类反馈:有人以 IP 访问时报不安全,有个批处理客户端报告证书过期,还有调用方偶尔握手失败。不要立即概括成“证书坏了”。先为每次失败收集时间、URL、客户端版本及信任环境、实际连接地址和原始错误,再标出停止在 TCP、TLS 还是 HTTP。浏览器“不安全”提示也要展开具体说明,单凭图标不能确定原因。

路径 A 从名称开始。用户输入的是 https://192.0.2.10/health,错误显示名称不匹配;证书 SAN 只有 api.example。这组证据足以解释该次失败:客户端期望 IP 身份,入口提供的是域名身份。改用正式域名访问,或用保留域名的 --resolve 测试。不要仅添加 HTTP Host,因为失败发生在发送 HTTP 请求之前。

若正式域名也拿到另一个站点的证书,再核对 SNI 与入口配置。可以在具备相应选项的 OpenSSL 环境中查看服务实际提供的证书链:

openssl s_client -connect 192.0.2.10:443 -servername api.example -showcerts -verify_hostname api.example -verify_return_error

其中 -servername 提供 SNI,-verify_hostname 要求名称核对,-verify_return_error 使验证错误影响握手结果。-showcerts 展示服务端送出的证书,展示本身不等于链已被信任。还要查看验证结果,并明确本机 OpenSSL 使用的 CA 信任来源;必要时指定受管理的 CA 文件,不能把不同工具的默认信任环境当成完全相同。

路径 B 检查时间。假设证书 notAfter 为 2026-11-12 00:00:00 UTC,可信当前时间是 2026-10-31 00:00:00 UTC,它还剩 12 天;某客户端却显示 2026-11-13。此时先修复客户端时间同步,再复测。若真实当前时间已经超过 notAfter,应该续期和部署证书。无论哪种情况,都要以可信时间和证书字段对照,而不是反复调整时钟直到错误消失。

还要检查 notBefore。新证书已经部署,但客户端时间明显落后,可能表现为尚未生效。保存证书后,可以查看其时间、SAN 与指纹,便于与线上入口对照:

openssl x509 -in leaf.pem -noout -dates -ext subjectAltName -fingerprint -sha256

这里的 leaf.pem 是已取得的终端实体证书文件。检查本地文件只能说明该文件内容,仍需确认它就是失败连接实际收到的证书,不能拿准备发布的新证书替代线上旧证书的证据。

路径 C 检查链与信任。若名称、时间正常,而 curl 报无法找到签发者或无法构建可信链,就对照服务实际发送的中间证书与客户端 CA 库。服务器遗漏中间链,应修复入口部署;客户端根库过旧,应按目标环境的维护流程更新。不能不加区分地把收到的任意证书加入信任库,这会改变信任边界,却未必修复部署错误。

链异常还可能来自中间设备。若企业代理终止 TLS 并重新签发证书,客户端看到的签发者可能与直连时不同。对照失败网络和正常网络中看到的证书指纹、签发者及入口路径,确认是否经过预期代理。若组织要求使用该代理,应核对受管理的信任配置;若路径不符合预期,则交由对应网络与安全责任人处理。

偶发失败要把“偶发”变成可比较的样本。假设 20 次请求中有 5 次失败,记录发现成功时都连到入口 A,失败时都连到入口 B;A 的证书还有 60 天,B 的证书已过期。用保留域名的定向连接稳定复现后,就能把问题指向 B 的轮换遗漏。25% 是本次样本的失败比例,不能据此断言整个负载分配长期固定为这个比例。

如果同样的入口也时好时坏,还应检查其背后是否有多个 TLS 终止节点,以及是否只有特定网络路径经过中间盒。握手失败也可能与协议兼容、连接限制或入口负载有关;当名称、时间、链的证据均正常时,应沿具体错误继续查,不能强行归入证书问题。交接材料最终要写成可复现事实:“某时刻、某 URL、某入口、某证书、某阶段失败”,并附对应的正常对照。

按证据分流名称、时间、信任链与偶发问题
按证据分流名称、时间、信任链与偶发问题

放进方案里

HTTPS 方案应先写明公开 URL、域名清单、端口、TLS 终止位置和目标客户端范围。验收必须开启正常证书校验,逐项确认 URL 名字与 SAN 匹配、证书处于有效期、服务端中间链完整、客户端能够建立到受信任根的路径。记录实际协商的协议版本,并与已批准的版本策略比较。随后执行能覆盖约定业务路径的 HTTP 探针,保存状态码与预期内容,避免只凭连接成功交付。

部署清单应覆盖所有对外入口、区域和实际发布的地址类型。轮换后从客户端重新观察证书,记录各入口的到期时间与指纹,确认新材料已经由运行中的服务提供。申请成功、文件上传成功和线上生效应分别检查。分批轮换需要有截止时间、遗漏节点检查和失败回退安排,避免某个入口继续使用旧证书。

续期监控要同时观察任务执行与线上证书剩余时间。阈值必须覆盖检测间隔、完整轮换窗口和合理余量:若轮换需 3 天、检测间隔为 1 天,只剩 1 天才告警显然不够。写明告警接收人、处理责任人和复测方式,并定期确认通知真的能够送达。私钥限制授权访问,明确存放、备份和泄露处置责任,不放进仓库、工单正文或普通日志。

明文传输仅限经过说明的例外,写明适用链路、原因、风险控制和复审条件;公网重定向不能替代最终 HTTPS 身份核对。内部链路若未使用 TLS,也要明确终止边界及相应保护依据,不能笼统标记为全链路加密。

故障交接必须携带阶段:TCP 是否建立、TLS 在名称还是时间或链校验处失败、HTTP 是否已返回及其状态。附请求时间、实际入口、客户端环境、原始错误和证书字段,敏感信息脱敏。用 -k 获得的响应只能作为明确标注的诊断补充,不能填入正常验收通过栏;修复后必须重新提交开启校验的结果。

10 小结:走向运维观测命令(S21)

TLS 为通道增加保密、完整性与身份核对;HTTPS 把 HTTP 放入该通道。名称、时间与信任链共同决定客户端能否接受证书,HTTP 探针则继续确认业务交付。生命周期管理把今天的成功延续到下次轮换。

进入 S21 后,我们会使用 Linux 网络命令观察地址、连接与请求。先判断失败阶段,再选择观测命令,得到的输出才有明确解释范围:连接证据回答 TCP 问题,握手证据回答 TLS 问题,响应证据回答 HTTP 问题。

下一课转向 S21 Linux 网络命令:把本章验收过的结论带进后续主题,写成可检查的方案约束。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务