🔗 Feature: SslBump 使用 Bump-Server-First 方法
- 目标: 允许对拦截的 SSL 连接进行加密。为模拟服务器证书详细信息做好准备。
- 状态:完成
- 版本: 3.3
- 开发者: AlexRousskov 和 Christos Tsantilas
- 更多: 需要 SslBump,启用 服务器证书模拟,并且在没有 动态证书生成 的情况下毫无意义
🔗 动机
此功能已在 Squid-3.5 中被 peek-n-splice 取代 |
第一个 SslBump 实现对于 HTTP CONNECT 请求工作良好,这些请求指定了 Squid 必须建立 TCP 隧道的服务器。当某些浏览器显式配置为使用代理时,它们会发送此类请求。在 SslBump 模式下处理这些请求时,Squid 可以使用提供的服务器名称 动态生成 一个服务器证书,然后冒充指定的服务器。这使得 Squid 能够获取来自客户端的真实 HTTP 请求(例如 HTTP GET 或 POST),解密它,并最终连接到真实服务器并转发请求。
上述方案在拦截 SSL 连接时会失败,因为拦截的连接以 SSL 握手开始,而不是 HTTP CONNECT 请求。因此,Squid 没有从客户端收到源服务器的名称。Squid 知道被拦截连接的目标 IP 地址,但 IP 地址不能用于 SSL 证书生成。这使得无法生成匹配的服务器证书。没有这样的证书,Squid 就无法冒充服务器。
当某些客户端(例如 Rekonq 浏览器 v0.7.x)发送使用 IP 地址而不是服务器名称指定隧道目标的 CONNECT 请求时,会发生非常类似的失败。
旧的“bump-client-first”方法的另一个问题是,每当服务器发送一个部分有缺陷或完全无效的 SSL 证书时,为时已晚,无法将该问题传播给客户端并让客户端处理。这很不理想,因为理想情况下,最终决定应该由用户做出,而不是 Squid,而且浏览器已经拥有相当复杂的工具来警告用户有关问题、检查无效证书、忽略问题、缓存用户决定等(我们并不真正想重复这些)。虽然此项目不会将证书问题转发给客户端,但这是支持 未来 中频繁请求的功能所需的步骤。
🔗 实现概述
为了加密拦截的 SSL 连接,此项目彻底改变了 Squid 中加密连接处理事件的顺序。当收到拦截的连接时,Squid 首先使用 SSL 连接到服务器并接收服务器证书。然后,Squid 使用真实服务器证书中的服务器名称生成一个伪造的证书,并冒充服务器,同时仍然使用与服务器已经建立的安全连接。
颠倒连接处理事件的顺序是一项复杂的任务,它会影响 Squid 核心代码的几个区域,并违反了代码中散布的一些基本 Squid 假设(例如,每个服务器连接都由一个 HTTP 请求支持)。在实现和初步测试期间发现了以下注意事项:
🔗 ACL 可用性
加密的连接会经过几个阶段。每个阶段都会影响哪些信息可用于各种 squid.conf ACL。
对于拦截的连接
- 当 Squid 连接到服务器以查看证书时,没有 HTTP 请求也没有服务器名称。在此阶段,使用源和目标 IP 地址/端口的 ACL 应该可以正常工作。
- 在 Squid 收到服务器证书后,实际的服务器名称变得可用(来自证书的 CN 字段)。Squid 在需要时使用该名称报告错误页面上的证书详细信息,但并不假定未来的请求将定向到同一个服务器。因此,在此阶段,目标域名 ACL 将无法正常工作。
- 在 Squid 收到第一个 HTTP 请求后,所有特定于 HTTP 请求的 ACL 都应该可用。对于每个请求,Squid 会验证请求的服务器名称是否与之前检索到的证书 CN 匹配。如果不匹配,将触发 SQUID_X509_V_ERR_DOMAIN_MISMATCH 错误,并终止与客户端的连接。
对于加密的 CONNECT 请求
- 当 Squid 连接到服务器以查看证书时,只有一个 CONNECT HTTP 请求。该请求可能有一个服务器名称,但有些浏览器使用 IP 地址而不是服务器名称(例如 Konqueror)。在此阶段,使用源和目标 IP 地址/端口的 ACL 应该可以正常工作。
- 在 Squid 收到服务器证书后,即使之前不可用,服务器名称也变得可用(来自证书的 CN 字段)。现在可以使用服务器域名 ACL。TODO:Squid 不检查 CONNECT 主机名是否与后续嵌入的 HTTP 请求 Host 值匹配。正确添加这些检查很困难,因为其中一个名称可能是特定于位置/CDN 的 IP 地址,并且隧道端点可能设计为使用多个主机名(例如,隧道的服务器端可能是代理)。
- 在 Squid 收到第一个加密的 HTTP 请求后,所有特定于 HTTP 请求的 ACL 都应该可用。对于每个请求,Squid 会验证请求的主机名是否与之前检索到的证书 CN 匹配。如果不匹配,将触发 SQUID_X509_V_ERR_DOMAIN_MISMATCH 错误,并终止与客户端的连接。
请注意,sslproxy_cert_error ACL 始终检查真实的服务器证书,而不是生成的伪造证书。
🔗 连接固定
在不加密的情况下,客户端会打开与源服务器的安全连接,并在该单个连接上发送/接收少量消息。在 Squid 加密其连接的情况下,合理地假设至少一些客户端依赖于该连接的目的保持不变。例如,在处理 NTLM 身份验证时,我们遇到了这种假设。但是,Squid 需要处理 *两个单独* 的连接(一个与客户端连接,另一个与不知情的服务器连接),因此 Squid 可能随时需要关闭服务器连接。Squid 尝试使用以下方法最大程度地减少服务器连接在客户端连接生命周期内发生更改的可能性:
- 在建立服务器连接以查看服务器证书时,Squid 会将服务器连接“固定”到客户端连接。后续的客户端请求将全部发送到该服务器连接,就好像 Squid 不存在一样。Squid 还记住查看过的服务器证书。
- 如果服务器关闭连接但客户端继续发送更多请求,Squid 会打开一个新连接到服务器并再次将其固定到客户端连接。此重新打开是必要的,以最大程度地减少兼容性问题,因为客户端未期望服务器关闭连接,因为 Squid 到客户端的连接信号与服务器到 Squid 的连接信号不同。TODO:将来,如果源服务器要求,我们可能会向客户端发送“Connection: close”。
- 在重新打开服务器连接时,Squid 会验证服务器 SSL 证书没有发生太大变化。如果服务器证书已更改,Squid 会响应此项目添加的 SQUID_X509_V_ERR_DOMAIN_MISMATCH 错误。此功能最大程度地降低了另一个攻击者在 Squid 已向客户端发送伪造的服务器证书并客户端批准该伪造证书后注入其自身到 Squid 后消息流中的可能性。
🔗 在处理 CONNECT 请求时为什么先加密服务器?
先加密服务器是处理拦截的 HTTPS 连接所必需的,但对于大多数 HTTP CONNECT 请求也应使用相同的方案,因为它与旧的 bump-client-first 方法相比具有一些优势。
- 当 Squid 知道有效的服务器证书详细信息时,它可以生成具有这些详细信息的伪造服务器证书。使用 bump-client-first 方案,所有这些详细信息都丢失了。总的来说,浏览器不关心这些详细信息,但可能会有一些 HTTP 客户端(甚至人类用户)需要或可以从了解它们中受益。
- 当服务器发送一个 *无效* 证书时,Squid 可能能够在其自己的伪造证书中复制这种无效性,从而使 HTTP 客户端能够控制是忽略问题还是终止事务。使用 bump-client-furst,很难支持类似的动态、用户导向的退出;当服务器证书无法验证时,Squid 本身必须决定如何处理。
- 当服务器请求 *客户端证书* 时,Squid 可能能够请求客户端,然后将客户端证书转发给服务器。使用 bump-client-first 方案可能无法进行此类客户端证书处理,因为它必须在 SSL 握手后完成。
- 一些客户端(例如 Rekonq 浏览器 v0.7.x)在 CONNECT 请求中不发送服务器名称。此类客户端即使在正向代理模式下也需要 bump-server-first。不幸的是,完全支持此类客户端还有其他问题(即 Squid 不知道 CONNECT 请求中的 IP 地址是否是用户在地址栏中键入的内容),因此除非添加更专业的检测代码,否则并非所有功能都对它们有效。
正在测试的代码对 CONNECT 请求使用 bump-server-first,但这种一刀切的决定是有争议的,并且可以选择使用 squid.conf ACL 使其可配置(这会使本已相当复杂的代码更加混乱)。欢迎反馈。
🔗 为什么不直接使用服务器名称指示 (SNI)?
而不是先加密服务器,可以在 SSL 或 TLS 握手期间使用 SNI 功能来获取预期的服务器名称。我们没有采取这种捷径,因为:
- 在运行 Windows XP 的 Internet Explorer 中没有 SNI 支持。
- 无法模拟服务器证书,以便用户可以(a)决定是否忽略任何证书问题,以及(b)缓存该决定(参见 服务器证书模拟)。
🔗 限制
此项目将不支持将 SSL 服务器名称指示 (SNI) 信息转发到源服务器,并且会使此类支持变得更加困难。然而,SNI 转发本身也存在 *严重* 的挑战(超出本文档范围),这些挑战远远超过了添加转发的困难。
Categories: Features
导航:站点搜索,站点页面,类别,🔼 向上