网络

浏览器代理认证与凭据安全管理

将代理凭据与网站登录分开,不让它们进入日志和源码,在代理 URL 中正确编码,并安全地解读 407 与 SOCKS 认证失败。

文档中心

想直接进入 网络 文档吗?

这篇文章属于博客内容库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

为什么代理凭据需要单独处理

通过需要认证的代理工作的浏览器,同时带着两套互不相关的凭据。代理凭据证明你的组织有权使用这条路由。目标站点登录证明某个人有权使用网站上的账号。它们由不同的主体签发,按不同的周期过期,一套泄露所需的应对也与另一套不同。把两者当作同一个“登录”,是许多凭据审查中的第一个错误。

这种区分既是概念上的,也是实际操作上的。代理凭据通常属于某个服务账号或服务商控制台,由使用该路由的任务共用。目标站点登录属于某个用户或测试账号,并在页面流量内部传输。如果两者放在同一个配置文件或同一个密钥中,轮换代理凭据可能让测试账号被锁定,而目标站点的密码变更又可能被误看成代理故障。请把它们放进各自独立的密钥,配上不同的负责人和独立的轮换记录。

这些建议适用于你的组织获准使用的代理,并遵循签发凭据的服务商的条款。这些建议不涉及寻找、猜测、共享或转售凭据,也不描述如何越过服务商的访问控制。如果某个凭据本就不该由你使用,任何处理方式都无法让这条路由变得可接受。

示意图:浏览器上下文向代理环节出示代理凭据,并向网站出示独立的目标站点登录,启动记录中只保留密钥引用和路由标签

协议名称也会带来预期。HTTP Basic 认证和 SOCKS5 的用户名与密码方法,都只是递交用户名和密码的方式。两者本身都不保证密钥的机密性:Basic 使用的编码任何人都能还原,SOCKS5 方法则按原样发送这些值,除非有其他手段保护连接。机密性取决于浏览器与代理之间的传输、服务商记录什么,以及你自己的工具如何处理这个值。这些环节在不同部署中各不相同,所以审查时应当逐一点名,而不是默认成立。

凭据可能泄露的位置比乍看起来要多。密钥可能进入版本控制,出现在进程列表可见的命令行里,出现在回显启动参数的任务日志中,出现在引用代理地址的错误信息里,出现在终端截图、支持工单或共享的配置文件中。凭据安全管理的做法是:针对其中每一处,决定密钥是否可以出现,然后检查它确实没有出现。

路由的设置见 代理配置,浏览器如何使用 CONNECT 隧道和代理头部见 HTTP 代理语义与浏览器请求。这里关注的是凭据本身:它如何递交、书写、存放、轮换,以及如何不让它出现在不该出现的地方。

如何解读 407、401 与 SOCKS 认证失败

当代理要求认证时,它会用状态码 407 Proxy Authentication Required 回应请求,并带上说明所接受方案的 Proxy-Authenticate 头部。客户端随后带着 Proxy-Authorization 头部重试。RFC 9110 定义了这两个头部和该状态码,MDN 上关于 Proxy-Authorization 和 407 的页面为 Web 开发者做了概述。关键在于质询属于谁:407 由代理发出,因此它指向路由中的代理环节。

401 Unauthorized 响应是目标站点自己的质询。它带有 WWW-Authenticate 头部,用 Authorization 头部作答。这两次交互可能发生在同一次页面加载中,各自使用自己的凭据。407 表示代理没有接受代理凭据。401 表示代理接受了这条路由,而目标站点没有接受登录。把两者混淆,会让排查指向错误的负责人和错误的密钥。

HTTPS 目标站点还多一个细节。浏览器先通过 CONNECT 请求让代理建立隧道,代理认证就发生在这个请求上。目标站点的 TLS 会话和任何 401 质询,都要等隧道建立之后才会出现。因此 HTTPS 页面上的代理认证失败,表现为隧道建立失败,往往发生在任何页面内容之前,而不是来自网站的响应。此外,Proxy-Authorization 是发给索要它的那个代理的,不会继续传给目标站点。

HTTP Basic 定义在 RFC 7617 中。客户端用冒号连接用户名和密码,再做 Base64 编码。Base64 是可逆的编码,并不提供保密性;该 RFC 指出 Basic 本身不提供机密性,应在受保护的连接上使用。由于冒号是分隔符,用户名不能包含冒号,而密码可以。当凭据含有这类字符时,它们在 URL 中怎样书写就变得重要。

SOCKS5 使用另一套交互。客户端与服务器就 RFC 1929 定义的用户名与密码方法达成一致后,客户端发送用户名和密码,各自最长 255 字节,服务器回复一个状态。状态为零表示成功。任何其他值都是失败,服务器会关闭连接。浏览器不会收到 407 页面,因为 SOCKS 没有 HTTP 状态码。SOCKS5 路由上的认证失败,表现为建立过程中连接被拒绝或被关闭,所以错误文本通常不如 HTTP 响应具体。该方法本身不会加密这些值。

对认证失败的有界报告只写明环节和类别,不写其他内容。例如:“路由 R 的代理认证被拒绝,已收到质询,未建立隧道”。其中不包含 Proxy-Authorization 的值、用户名、完整的代理 URL 或服务商的响应正文。把结果报告为认证错误,而不是超时或笼统的网络故障,才能让下一步动作保持正确:检查凭据及其有效期,而不是 DNS 配置或目标站点。

重复重试需要设定上限。提供了正确凭据后仍返回 407 的路由,通常会继续如此,大量快速重试还可能锁定账号或触发服务商一侧的保护。按你自己的策略在少量尝试后停下,把路由标记为认证类失败,并交给能够续期或轮换凭据的负责人。不要为了碰运气找到可用的凭据,去尝试没有分配给该路由的其他凭据。

如何把凭据写入代理 URL

代理 URL 遵循 RFC 3986 中的通用语法。凭据放在主机之前的 userinfo 部分:方案、用户名、冒号、密码、at 符号、主机和端口。同一份 RFC 将 userinfo 中的“用户名加密码”形式标记为不推荐,因为以明文传递认证信息已被证明存在安全风险。这是要谨慎处理该字符串的理由,而不是换一种写法的理由,因为许多启动接口期望的正是这种形式。

URL 中有若干字符具有特殊含义,出现在用户名或密码里时必须做百分号编码。at 符号会结束 userinfo,所以密码里字面的 at 符号会被当作主机的开头。斜杠、问号和井号会结束 authority 部分。百分号用来开始一个编码值,所以字面的百分号本身也必须编码。冒号用来分隔用户名和密码。对一个字符编码,就是把它替换成百分号加两位十六进制代码。

例如,密码 p@ss:word 在 URL 中写作 p%40ss%3Aword,完整的值可能是 http://svc_user:p%40ss%3Aword@proxy.example.com:8080。该示例中的主机只是文档占位符。应分别对用户名和密码编码,然后再拼装 URL,因为对已拼好的 URL 整体编码会连分隔符一起改掉。使用所用语言标准库中经过测试的辅助函数,比手写替换更安全,例如 JavaScript 中的 encodeURIComponent,或 Python 中把安全字符集设为空的 urllib.parse.quote。

各工具解析 userinfo 的方式略有差异,所以在一个客户端里可用的值,换到另一个客户端可能失败。编码之后,请在实际使用它的工具里测试这条确切的字符串,并把解析错误当作配置问题。不要为了让失败的字符串通过而削弱编码。含有加号、空格或非 ASCII 文本的密码值得单独测试,因为不同编码器对这些字符的处理并不一致;在 URL 中空格写作 %20,加号则属于表单编码。

错误的编码会造成误导性的失败。如果未编码的 at 符号把 userinfo 切在了错误的位置,工具可能把被截断的密码发给代理并收到 407,也可能去解析一个不存在的主机并报告网络错误。这两种情况下,凭据本身都没有问题。当失败恰好出现在修改密码之后,先比较编码后的字符串,再去怀疑服务商。

如果服务商支持其他认证方式,例如接受来自已批准来源地址的请求,你也许可以用不带 userinfo 的 URL 启动。这样可以把密钥从命令行中移除,但信任转移到了该地址及其负责人身上,所以应把它记录为另一种方式,并配上自己的负责人和审查。有些服务商不提供这种方式,能否使用由服务商的条款决定。

不要把字面的 URL 留在会长期保存的地方。源文件、容器镜像、shell 历史和工单评论,会在凭据变更很久之后仍保留这些值。这些地方应当放的是对密钥的引用,而不是值本身。

存放、轮换与遮蔽凭据

把每个代理凭据存放在你的组织已经掌控的密钥管理器或同类存储中,并按名称引用它。启动记录里于是包含路由标签,例如“regional-checks-eu”,以及密钥引用,比如已存条目的名称,而不是字面的代理 URL。读到这条记录的人能知道用了哪条路由、由谁负责,却无法使用它。

尽可能晚地解析这个引用。启动器取得该值,对用户名和密码编码,拼出 URL,启动浏览器,并且不在自己的状态或日志中保留该值。在尽可能小的范围内构造 URL,不要把它写入比这次启动存活更久的文件。这也让同一份任务定义在轮换之后依然有效,因为变化的只有存储的值。

要如实看待命令行参数会暴露什么。启动参数对同一主机上能列出进程的其他账号可见,还可能被监控代理、崩溃报告或容器运行时的检视输出捕获。因此把凭据嵌入参数,并不会让它在该主机上变成私密的。应限制谁可以登录这台机器并读取其进程信息,并在服务商支持时优先选用只限于一条路由、权限受限且有效期短的凭据。

遮蔽是一项独立的控制,必须测试而不是假定。任务日志、包装脚本、错误处理器和测试报告,往往会在出错时打印启动参数。在写入任何一行之前先遮蔽 userinfo,用固定标记替换密码,并且同时遮蔽编码后的形式,因为日志里可能出现其中任何一种。启动失败之后,读取实际的输出文件和进程列表,在其中搜索用户名和密码的原始形式与编码形式。

轮换需要负责人和计划。确定每个凭据多久更换一次、谁可以更换,以及新值如何送达启动器。替换存储的值,重新启动使用该路由的上下文,并确认旧值不再可用。因怀疑泄露而触发的轮换,还需要审查旧值曾被写到哪里,因为在服务商吊销之后,日志和工单仍可能保留它。

给每条路由配置自己的凭据和自己的引用。当一个共用凭据服务多条路由和多个上下文时,一次轮换会同时中断这些路由,一次泄露会让其中每一条都暴露。独立的引用让你可以更换某一条路由的凭据,而其他路由继续使用各自的凭据运行。如果服务商把凭据与套餐或子账号绑定,就在你自己的命名中体现同样的结构。

服务商一侧的事实由服务商掌握。服务商保留请求日志多久、是否记录用户名、与代理之间的连接是否受 TLS 保护,以及凭据如何过期,都因服务商而异。请以书面方式询问这些问题,并把答复与该路由一并记录。不要认为 URL 中看起来安全的方案就意味着第一段连接已加密:HTTPS 代理保护的是到代理的连接,HTTP 代理则不保护,SOCKS5 路由需要单独评估。

不要把目标站点的登录放进这个存储。目标站点账号的密码属于账号所有者和执行登录的应用,有它自己的存放和轮换方式。把它放在代理凭据旁边,会扩大可读取其中每一个的人群,还把两个互不相关的轮换绑在了一起。

运维评估

把凭据处理视为一项在变更之后要验证的属性,而不是一次完成的设置步骤。在更换服务商、变更套餐、轮换凭据、更新启动器、调整日志、浏览器主版本更新或新增路由之后,都要重复这项审查。记录你检查了什么、路由标签、密钥引用和结果,但不要把密钥抄进记录。

像分配路由一样分配角色。路由负责人决定哪个凭据服务于哪个上下文,并批准轮换。平台负责人掌管密钥存储和日志管道。应用负责人定义路由不可用时用户看到什么。这三方之间一次简短的交接,比一份粘贴了凭据的共享文档更能说明问题。

在认证失败发生之前,先定义它的可见结果。任务可以停止并给出明确消息,说明路由需要处理;功能可以显示不可用状态;在你的策略允许时,也可以由获批的备用路由接手。恰好能加载页面的直连并不是隐含的备用方案。它在没有决策的情况下改变了网络边界和流量的来源,所以不应成为凭据失败后悄无声息的结果。

BotBrowser 支持把代理凭据嵌入 --proxy-server URL,适用于 HTTP、HTTPS、SOCKS5、SOCKS5H 和 QUIC 路由,并对特殊字符使用百分号编码,因此运维人员可以在启动时提供获批路由的凭据,而无需 page.authenticate()。BotBrowser 不会提供密钥存储、轮换或日志遮蔽,也不会让嵌入的凭据对进程列表或服务商日志保持私密;这些控制仍由你的部署工具和代理服务商负责。

按上下文路由有助于让各个引用保持独立。当同一个浏览器部署服务多个获批的工作流时,每个上下文可以使用自己的路由,从而使用自己的凭据,如 按上下文配置代理 所述。该设置只是在你的部署已经验证过的路由中进行选择;它不会签发凭据,也不会改变服务商允许的范围。

当某条路由认证失败时,负责任的做法是通过其负责人续期或轮换凭据、改选另一条获批路由,或者停止该工作流。在别处搜寻凭据、借用其他团队的凭据,或未经批准改用另一个服务商账号,都不在其列。记录中应能看出采取了哪种做法以及由谁采取。

状态消息和支持回复应当与记录使用同一套措辞。“代理认证被拒绝”比“网络错误”更精确、更有用,“需要目标站点登录”则提示应由另一位负责人处理。措辞精确,可以避免事件被派给错误的团队,也能让密钥远离许多人都能阅读的工单。

执行凭据安全检查

对每条路由执行这些检查,并为每一项记录通过或未通过,不要把密钥抄进记录。

  1. 把一个失败的请求追溯到它所在的环节。如果带有 Proxy-Authenticate 质询的 407 被归于代理环节,带有 WWW-Authenticate 质询的 401 被归于目标站点,并且记录中既没有 Proxy-Authorization 的值也没有 Authorization 的值,则通过。如果两者被合并成一个“登录失败”类别,则未通过。
  2. 阅读启动记录。如果其中显示的是路由标签和密钥引用,则通过。如果其中包含字面的代理 URL、用户名或密码,则未通过。
  3. 在一次正常启动和一次故意失败的启动的任务日志、错误输出和进程列表中,搜索用户名和密码的原始形式与百分号编码形式。如果日志和错误输出没有匹配,且进程列表没有匹配或仅限你批准的账号读取,则通过。如果凭据出现在日志、错误输出或其他账号可读的进程列表中,则未通过。
  4. 用含有保留字符的测试密码启动,例如 at 符号、冒号、斜杠和百分号。如果密码在 URL 中已做百分号编码且该路由认证成功,则通过。如果只有削弱编码之后连接才能工作,则未通过。
  5. 故意提供错误或已过期的凭据。如果运行在你设定的重试上限内停止,并针对所指明的路由报告认证错误,则通过。如果报告的是超时、DNS 错误或笼统的网络故障,或者无上限地重试,则未通过。
  6. 轮换其中一条路由的凭据。如果该路由用新值认证成功、旧值被拒绝,并且其他路由和上下文继续使用各自的引用正常工作,则通过。如果轮换一条路由改变了另一条路由的结果,则未通过。
  7. 在更换服务商、变更套餐、更新启动器或浏览器主版本更新之后,重复检查 1 到 6。在重复运行通过之前,保留最近一次被接受的记录。

来源

#代理#SOCKS5#Http#网络#配置

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。