跳转到内容

Web 搜索多提供商与 SSRF 防护

Web 搜索多提供商与 SSRF 防护

学习目标

本章聚焦 LibreChat 的 Web 搜索系统。你将了解:

  • 多提供商搜索的认证与聚合策略
  • SSRF 防护的 fail-closed 设计
  • 用户提供 URL 的验证流程

前置知识

下文假设你已理解上述概念,直接聚焦 LibreChat 的具体实现。


项目实践

多提供商认证

loadWebSearchAuth 函数遍历所有配置的搜索类别(search、scraper、reranker),验证每个提供商的认证信息:

for each category-service combination:
for each auth field:
if value is ${ENV_VAR} 形式:
从 DB 加载用户提供的值
else:
使用系统定义的值
if at least one service in category is authenticated:
category.authenticated = true

认证类型分为两种:

类型含义
USER_PROVIDED使用用户自己配置的 API Key 或 URL
SYSTEM_DEFINED使用管理员在 .env 或 YAML 中配置的值

SSRF 防护

isSSRFUrl 函数实现了 fail-closed 的 SSRF 防护:

isSSRFUrl(url):
try:
parsed = new URL(url)
catch:
return true // 无法解析 → 阻止
if parsed.protocol 不是 http/https:
return true // 非 HTTP 协议 → 阻止
if isSSRFTarget(parsed.hostname):
return true // 已知内部地址 → 阻止
return resolveHostnameSSRF(parsed.hostname) // DNS 解析检查

防护层级

  1. URL 解析失败 → 阻止(防畸形 URL 绕过)
  2. 非 HTTP(S) 协议 → 阻止(防 file://gopher:// 等)
  3. 已知内部地址黑名单 → 阻止(127.0.0.1169.254.169.254 等)
  4. DNS 解析检查 → 阻止 DNS rebinding 攻击

用户提供 URL 的验证

两类 URL 字段的处理策略不同:

类型处理方式
普通用户提供 URL(如 searxngInstanceUrlSSRF 预检通过后允许
需管理员启用的 URL(如 tavilySearchUrl需要 USER_PROVIDED_OPT_IN_URL_KEYS 中的显式启用

这给了管理员对敏感 URL 字段的精细控制——即使配置允许用户提供值,也需要管理员显式启用。


问题与规避

问题 1:用户配置的 URL 指向内部服务

规避:SSRF 防护的 resolveHostnameSSRF 函数对 hostname 进行 DNS 解析,检查解析结果是否为内部地址。这防止了 DNS rebinding 攻击——即使 URL 的 hostname 看起来是外部地址,DNS 解析后可能指向内部 IP。

问题 2:无法解析的 URL 格式导致异常

规避isSSRFUrl 在 try-catch 中解析 URL,解析失败返回 true(阻止),不会抛出异常。

问题 3:认证字段包含未解析的环境变量占位符

规避extractWebSearchEnvVars 函数提取 ${VAR} 格式的变量名,在认证前检查环境变量是否存在。缺失的变量导致认证失败,而非传递未解析的占位符。


设计取舍

Fail-Closed vs Fail-Open SSRF 防护

方案优势代价
Fail-Closed(默认阻止)安全,不会误放内部地址可能阻止合法的外部 URL
Fail-Open(默认允许)不会误拦安全风险高

LibreChat 选择了 fail-closed,因为搜索系统处理的是用户输入的 URL,安全优先级高于可用性。


参考来源