# TLS 证书故障：代理响应流程

代理 API 调用中的证书错误是停止信号，不是普通的网络小故障。该调用可能携带 bearer token、签名请求、客户记录，或会改变生产状态的指令。如果客户端无法确认连接另一端的所有者，就不该发送这些内容中的任何一个。

这类问题很容易被错误处理，因为人在压力下看到证书过期，就会去使用不安全的开关。自主代理会让这个捷径更加危险。它可能快速重试，遵循仓库 issue 中隐藏的指令，还可能使用多个凭据重复同一个不安全调用。好的响应流程会停止运行，区分事实与猜测，只有在有人证明预期端点重新成为实际接收流量的端点后，才恢复访问。

## 证书警告会改变信任决策

TLS 有两个经常被混为一谈的作用。它会加密流量，也会验证服务器身份。没有身份验证的加密，只是给攻击者和你的客户端提供了一段私密对话。

RFC 6125 介绍了应用如何将服务身份与参考标识符进行核对，参考标识符通常是客户端原本想访问的 DNS 名称。RFC 5280 介绍了证书路径验证：客户端从对方提供的叶证书开始，经过中间证书，建立一条通往信任锚的证书链。这两项检查都很重要。证书签名有效，并不代表它属于 `api.example`，而不是 `api-attacker.example`。证书包含正确的主机名，也不代表它的链条一定通向组织批准的颁发者。

对代理流量来说，后果很具体。注入 HTTPS 请求的 bearer token 会被终止连接的人拿到。使用客户端凭据签名的请求可能授权状态变更。冒充者返回的 API 响应可能指导后续操作，或污染代理的工作上下文。请求经过加密，并不会减轻这些风险。

在团队确定原因前，应将以下消息视为与安全相关：

- 证书已过期或尚未生效
- 主机名或主题备用名称不匹配
- 无法获取本地颁发者证书，或无法验证第一张证书
- 证书链中包含自签名证书
- 代理、DNS 或网络变更后证书验证失败

不要把每次失败都判断为正在发生的攻击。遗漏中间证书、笔记本电脑时钟错误，都是常见的运维故障。但响应应从不确定性出发，因为有人重定向流量或安装未经批准的拦截代理时，也会出现同类错误。

TCP 连接成功，只能证明某个地址上有东西作出了响应。禁用验证后 TLS 握手成功，证明的事情更少。服务器身份检查是带凭据的 API 调用获得离开本机许可的关键环节。

## 收集线索前先停止受影响的运行

第一项运维动作，是阻止继续向失败目标发送带凭据的调用。暂停代理进程；如果操作层支持撤销，就撤销其活动会话；或者移除它执行该操作的权限。先做这些，再花一小时争论证书是否看起来熟悉。

完整、原样保存原始错误。记录完整主机名和端口、不含授权头的请求方法、带时区的时间戳、客户端运行时及版本，以及发起调用的进程。记录机器使用的网络，以及故障是否发生在部署、证书续期、VPN 变更、代理上线、DNS 更新或设备管理变更之后。

如果证据包含客户路径或请求正文，不要把它随意放进聊天记录。绝不要将授权头、Cookie、客户端私钥或完整环境文件粘贴到事件工单中。调查证书不能成为制造第二次秘密泄露的理由。

使用一份简短的事件记录，明确责任归属：

1. 指定一名能够停止代理的响应人员，以及一名负责端点或网络路径的响应人员。
2. 标记该调用是否已经到达凭据注入点。如果到达过，在 TLS 对端得到识别前，应假设凭据可能已经暴露。
3. 保存代理会话标识符和失败操作记录，然后阻止该目标的自动重试。
4. 设定恢复审核时间点。需要 API 正常工作的人，不应成为唯一决定新根证书是否可信的人。

这不是形式主义。我见过团队在事件期间花大量时间测试命令，而后台工作程序仍每隔几秒重试同一个失败端点。诊断工作反而成了暴露的一部分。先停止行为。

不要让代理通过编辑全局运行时设置来“修复”信任问题。提示代理使用 `curl -k`、`verify=False`、范围过大的自定义 CA 包或禁用 Node.js 验证，应像要求打印 API token 一样处理。代理不能因为网络搜索说某个错误很常见，就判断意外证书是获批准的变更。

## 保存证书，但不要发送秘密

无需向 API 进行身份验证，也可以检查服务器证书。如果有未认证的健康检查端点，可以使用它；也可以建立握手，并在任何应用请求之前停止。检查应从出现故障的同一台机器和同一网络发起，因为代理设置、企业信任根和分离 DNS 在不同机器上往往不同。

下面的命令会通过 SNI 请求预期的服务器名称，检查指定主机名，显示对端发送的证书，并在验证出现问题时失败：

```sh
openssl s_client \\
  -connect api.example.com:443 \\
  -servername api.example.com \\
  -verify_hostname api.example.com \\
  -verify_return_error \\
  -showcerts </dev/null
```

正常结果的末尾会类似这样：

```text
Verification: OK
Verify return code: 0 (ok)
```

如果失败，将输出保存为受限的事件证据。值得关注的字段包括主题、颁发者、`Not Before` 和 `Not After` 日期、主题备用名称，以及证书链中的每张证书。PEM 区块可以让端点负责人比较你的机器收到的内容与服务器应提供的内容。它们是公开证书，但仍应谨慎处理记录，因为主机名和内部颁发者可能泄露基础设施细节。

然后在不带授权头的情况下测试正常客户端行为：

```sh
curl --verbose --fail --show-error \\
  https://api.example.com/health
```

详细输出会显示 TLS 版本、选中的证书和验证消息。如果请求路径会暴露租户名称，请将其脱敏。不要添加 `-k` 来让命令成功。验证失败正是你需要的证据。

常见的诊断错误是省略 `-servername`。许多托管端点会根据 SNI 选择证书，客户端省略它时，服务器会返回默认证书。这样产生的主机名不匹配，可能从未影响真正的客户端。另一个错误是使用不带 `-verify_hostname` 的 `openssl s_client`，然后把证书链结果当成主机名匹配的证明。这不是证明。

使用常规 DNS 诊断工具单独记录解析出的地址。不要把地址匹配当成完整测试。大型服务商合法地使用许多地址，而攻击者也可以控制一个看起来合理的 DNS 响应。证书名称、颁发者路径、网络路径和获批准的变更记录放在一起，才能说明真实情况。

## 选择修复方式前先对故障分类

证书错误在日志中看起来相似，但修复方式不同。先根据证据分类，再修改信任配置。否则团队可能只解决表面错误，却留下原本的路由或部署故障。

证书过期通常说明端点运营方错过了续期，或没有部署续期后的叶证书。用可信时钟检查捕获证书的有效期。时钟错误会让当前证书看起来已经过期或尚未生效，因此在要求服务商轮换证书前，先核对客户端时间。

主机名不匹配，说明服务器没有证明它拥有客户端请求的名称。常见原因包括错误的 API 基础 URL、SNI 配置错误、默认虚拟主机、过时的 DNS，或针对其他名称生成证书的代理。不要因为备用名称看起来相似就接受它。`api.example.com` 和 `api.internal.example.com` 可能代表不同的服务和信任边界。

未知颁发者可能表示客户端缺少合法的私有根证书或中间证书，也可能表示未经批准的 TLS 检查设备签发了替代叶证书。通过独立渠道，将颁发者和证书链与端点负责人发布或记录的部署信息进行比较。来自产生错误的同一系统的聊天消息，不是独立确认。

不完整的证书链通常是端点的问题。服务器必须发送叶证书和所需的中间证书，客户端只应预先拥有信任根证书。在本地安装缺少的中间证书，可能只掩盖一台机器上的服务器配置错误，却让其他调用方继续失败。应要求服务器负责人提供正确的证书链。

自签名叶证书需要格外谨慎。在内部开发服务中它可能正常，但本身不提供公共身份证明。如果管理员通过获批准的设备或工作负载信任存储分发私有 CA 的根证书，并记录 CA 的控制者，那么受管理的私有 CA 可以正常工作。端点负责人通过邮件发来一张随机自签名证书，不等于建立了信任体系。

证书撤销需要谨慎处理。许多客户端在所有环境中都无法可靠执行在线撤销检查，网络故障也会影响这些检查。不要因为基础握手返回 `0 (ok)`，就声称证书安全。如果负责人报告私钥泄露或证书已撤销，应阻止目标、轮换暴露的凭据，并部署替代证书。

## 区分服务器路径与代理路径

受管理的代理可以合法终止 TLS 并签发替代证书，但这必须明确记录。如果机器突然看到陌生的企业颁发者，不要因为主题中包含公司名称，就认定它无害。确认代理是否上线、其获批准的根证书、作用范围，以及 API 的数据和凭据是否允许经过它。

检查调用环境中的代理配置，但不要输出可能包含凭据的值。在类 Unix shell 中，下面的命令只打印已配置代理变量的名称：

```sh
for n in HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy; do
  [ -n "${!n}" ] && printf '%s is set\\n' "$n"
done
```

代理设置可以解释为什么同一个 API 在移动网络上正常，在办公网络上却失败。但它不会自动批准该代理。只有在组织规则允许时，才能在获授权的备用网络上比较结果。如果证书颁发者随网络变化，记录这一结果并交给网络负责人。

还要检查名称解析和路由变化。迁移后过时的 DNS 记录可能将调用导向仍提供已退役证书的旧负载均衡器。分离 DNS 可能向办公设备返回带私有证书的内部地址，向其他位置返回公共地址。这些设计可以修复，但客户端必须信任与路由相匹配的颁发者，同时仍然匹配请求的主机名。

不要通过硬编码 IP 地址绕过问题。HTTPS 身份检查使用 DNS 名称，直接使用 IP 可能绕过预期路由、导致 SNI 选择失败，或掩盖 DNS 控制问题。为严格受控的诊断测试临时覆盖 IP 有时有用，但必须得到端点负责人的批准，绝不能成为代理的永久配置。

代理还会带来证书错误本身无法回答的数据治理问题。如果代理终止 TLS，它就能读取 API 正文和注入的授权材料。安全团队必须决定该 API 是否允许这种检查。端点重新可用，并不能说明新路径是否可以接受。

## 让私有 CA 信任范围小而且可审查

私有证书颁发机构能解决内部 API、开发环境和受控服务网格中的实际问题。但如果团队非正式地分发根证书，又忘记哪些进程信任它们，私有 CA 就会带来风险。根证书持有者可以在其信任域内广泛签发身份，因此添加根证书是一次管理安全变更，不是兼容性调整。

通过负责该调用的受管理操作系统、容器镜像或应用信任存储安装获批准的私有根证书。记录根证书的主题、指纹、负责人、预期 DNS 后缀、批准日期和移除路径。在运行时允许的情况下限制信任范围。开发 CA 不应悄悄被每台机器上的所有生产代理信任。

不要把信任单张叶证书当成缺少 CA 流程的捷径。叶证书信任会在续期时失效，也会诱使人们把 PEM 字符串粘贴进代码仓库。它还让人难以区分有意的端点轮换和意外替换。如果两端都由你控制，应建立包含受保护签名材料、明确签发流程和计划轮换的私有 CA 流程。

双向 TLS 还增加了一个独立的身份方向。在普通 HTTPS 中，客户端验证服务器证书。在 mTLS 中，服务器还会向客户端请求证书。`alert certificate required` 或 `bad certificate` 这类错误，可能表示客户端没有提供自己的证书、提供了错误颁发者签发的证书，或缺少匹配的私钥。这并不意味着关闭服务器验证会有帮助。

将客户端私钥放在提示词、shell 片段和代理工作区之外。通过操作系统存储或其他受保护机制，让受控调用方访问客户端凭据。如果客户端证书或其私钥可能经过未验证的 TLS 连接传输，应按照颁发 CA 的流程撤销或替换。它与服务器证书的处理方式不同，但同样不能掉以轻心。

证书固定也是一个容易因好意造成中断的领域。固定叶证书会将客户端绑定到某一张具体的续期证书，因此正常轮换也可能让所有代理停止工作。对于范围狭窄且风险较高的集成，如果运营人员维护备用固定值并测试轮换，证书固定可能有意义。大多数团队通过强制主机名验证、管理获批准的信任根和监控证书过期时间，可以获得更好的结果。

## 经过独立检查后再恢复访问

只有在有人脱离失败的代理工作流验证修复后，才恢复代理。检查必须使用实际主机名、预期信任存储和执行正常验证的客户端。浏览器访问通常不够，因为浏览器和命令行客户端可能使用不同的代理设置和信任存储。

对于公共 API，应确认提供的证书链通向预期的公共信任根，主题备用名称包含配置的主机名，并由端点负责人通过有记录的变更确认部署或续期。对于私有 API，应验证获批准的私有根证书、颁发者、主机名和预期网络路径。将成功的命令结果与原始失败并列保存。

然后通过代理将要使用的同一路径，执行一次影响很小的身份验证操作。选择只读端点，或 API 提供的无害身份检查。若条件允许，同时查看操作日志和服务器端审计记录。确认目标主机名、响应状态和使用的凭据身份。不要把大批量任务作为第一次测试。

如果之前的失败使 token、签名请求或 mTLS 凭据有可能到达冒充者，应在恢复正常工作前轮换该凭据。团队常因证据不完整而抗拒轮换，但正确做法恰恰相反。正因为证据不完整，经过未验证连接的凭据才必须按已暴露处理。

准确记录修复内容，不要只写“TLS 已修复”这样的模糊结论。合格的关闭记录应说明端点运营方部署了中间证书、受管理信任存储加入了某个获批准的根证书，或移除了某项代理配置。还应记录谁验证了主机名，以及哪些代理运行恢复了。这样下次证书续期时，响应人员不会重新引入不安全的覆盖设置。

## 让安全路径比绕过更容易

如果安全处理需要数小时，而绕过只需设置一个环境变量，这套流程就会失败。构建代理集成时，应确保它无法选择不安全的 TLS 模式，无法编辑全局信任配置，也无法在排查问题时看到长期凭据。将连接设置、获批准的主机名和信任材料纳入正常变更审核。

Sallyport 可以将 API 凭据保存在加密保管库中，并替代理执行 HTTP 调用，因此代理不会收到凭据本身。它的会话和调用记录还可以保存谁尝试执行了该操作，但仍需要由人判断证书变化是获批准的变更，还是危险的路由。

针对重复的验证失败和调用已知绕过方式的尝试设置告警。失败调用应包含足够的结构化上下文，让响应人员能够识别主机名、故障类别和代理运行，同时不包含秘密。为代理设定固定指令：遇到证书验证错误就停止，报告准确错误和端点，并请求人工审核。不要让它寻找变通办法。

最值得先做的修复通常很普通：移除脚本中已有的不安全验证开关，让正常客户端使用受管理的信任存储。然后在生产环境替你执行证书续期前，先测试一次。演练过一次续期和一次意外颁发者事件的团队，会比唯一的 TLS 方案是从论坛复制命令的团队更快作出响应。

证书警告在阻止本不该继续的请求时，就完成了它的职责。保持这种状态。
