Files
FastGPT/.codex/issue/ssrf-vulnerability-fix.md
T
Ryo 5ccdcc1cd4 chore: backport selected main commits to v4.14.x (#6840)
* chore: bump pro submodule for hydration stability (#6808)

* sandbox-sync-agent

* refactor: host pro as submodule

* chore: checkpoint host pro restructure

* refactor workspace test layout and startup init

* chore: update next turbopack setup

* chore: snapshot current work before actions fix

* chore: update pro submodule

* chore: point pro submodule url to upstream https

* fix: Dockerfile

* chore: update pro submodule

* ci: support private pro submodule token and skip fork jobs

* fix(ci): build sdk workspace deps before code-sandbox bundle

* fix(app): exclude vitest configs from production typecheck

* fix(app-image): build sdk packages before next build

* fix(ci): align dockerfiles with workspace sdk build flow

* chore(docker): upgrade node20 docker images to node24

* fix(ci): read admin coverage output path in pro test workflow

* fix(app-image): include next-i18next config and locale assets

* chore: update pro submodule

* chore: do not specify branch for submodule

* chore: remove most ts-nocheck sign

* chore: update pro submodule

* chore: remove sandbox-agent-sync package

* chore: do not modify "pushData" file logic

* fix: health check

* chore: restore dev axios proxy state

* fix: test-fastgpt report workflow

* fix: use valid vitest coverage action inputs

* update shell (#6830)

* .codex (#6832)

* fix: home chat file uploads (#6838)

* chore: update actions workflow yamls

* chore: update turbo.json

* fix: split admin preview image workflows

* fix: allow home chat file uploads

* chore: add skip file type check env (#6839)

* chore: update actions workflow yamls (#6835)

* chore: update actions workflow yamls

* fix: allow pro workflows on fork pull requests

* chore: update turbo.json

* fix: split admin preview image workflows

* chore: bump pro submodule for admin typecheck

* chore: update pro submodule

* chore: bump pro submodule for turbo ignore

* chore: update pro submodule for file download api

---------

Co-authored-by: Archer <545436317@qq.com>
2026-04-28 18:00:31 +08:00

5.9 KiB
Raw Blame History

SSRF 漏洞修复设计文档

漏洞概述

漏洞编号: GHSA-6g6x-8hq5-9cw4 漏洞类型: Server-Side Request Forgery (SSRF) - CWE-918 严重程度: High 影响版本: <= 4.8.22

漏洞详情

1. 主要问题

FastGPT 的 HTTP Tool 连接器在处理用户控制的 URL 时缺乏 SSRF 保护:

受影响文件:

  • packages/service/core/app/http.ts (lines 127-166) - runHTTPTool() 函数
  • projects/app/src/pages/api/core/app/httpTools/runTool.ts - API 端点

问题代码:

export const runHTTPTool = async ({ baseUrl, toolPath, method, ... }) => {
  const { data } = await axios({
    method: method.toUpperCase(),
    baseURL: baseUrl.startsWith('http') ? baseUrl : `https://${baseUrl}`,
    url: toolPath,
    // 没有任何 IP 验证!
  });
};

2. 次要问题

isInternalAddress() 函数默认被禁用:

文件: packages/service/common/system/utils.ts (line 142)

if (process.env.CHECK_INTERNAL_IP !== 'true') {
  return false;  // 默认允许内部地址!
}

这意味着 http468 工作流节点和 readFiles 也缺乏 SSRF 保护,除非显式设置 CHECK_INTERNAL_IP=true

攻击场景

认证用户可以使用 HTTP Tool 进行以下攻击:

  1. AWS 凭证窃取:

    • baseUrl: http://169.254.169.254
    • toolPath: /latest/meta-data/iam/security-credentials/
  2. Kubernetes 密钥泄露:

    • baseUrl: http://kubernetes.default.svc
    • toolPath: /api/v1/namespaces/default/secrets/
  3. 内部网络扫描和服务利用

修复方案

方案 1: 在 runHTTPTool 中添加 SSRF 保护(推荐)

修改文件: packages/service/core/app/http.ts

runHTTPTool 函数中,在发起请求前添加 URL 验证:

export const runHTTPTool = async ({
  baseUrl,
  toolPath,
  method = 'POST',
  params,
  headerSecret,
  customHeaders,
  staticParams,
  staticHeaders,
  staticBody
}: RunHTTPToolParams): Promise<RunHTTPToolResult> => {
  try {
    // 构建完整 URL
    const fullBaseUrl = baseUrl.startsWith('http://') || baseUrl.startsWith('https://')
      ? baseUrl
      : `https://${baseUrl}`;

    // SSRF 保护:验证 URL 是否指向内部地址
    const fullUrl = new URL(toolPath, fullBaseUrl).toString();
    if (await isInternalAddress(fullUrl)) {
      return { errorMsg: 'Access to internal addresses is not allowed' };
    }

    const { headers, body, queryParams } = buildHttpRequest({
      method,
      params,
      headerSecret,
      customHeaders,
      staticParams,
      staticHeaders,
      staticBody
    });

    const { data } = await axios({
      method: method.toUpperCase(),
      baseURL: fullBaseUrl,
      url: toolPath,
      headers,
      data: body,
      params: queryParams,
      timeout: 300000
    });

    return { data };
  } catch (error: any) {
    return { errorMsg: getErrText(error) };
  }
};

方案 2: 修改 CHECK_INTERNAL_IP 默认值

修改文件: packages/service/common/system/utils.ts

将默认行为从"允许"改为"拒绝"

// 3. 如果未启用内部 IP 检查,则默认拒绝(安全优先)
if (process.env.CHECK_INTERNAL_IP === 'false') {
  return false;  // 显式禁用检查时才允许
}

// 默认启用内部 IP 检查

注意: 这个改动可能影响向后兼容性,需要在文档中说明。

方案 3: 添加 DNS Rebinding 保护(可选增强)

isInternalAddress 函数中,可以添加 DNS rebinding 保护:

  1. 解析域名获取 IP
  2. 验证 IP 是否为内部地址
  3. 在实际请求时,固定使用已验证的 IP(而不是重新解析)

这需要修改 axios 请求的方式,使用已解析的 IP 而不是域名。

实施步骤

第一阶段:核心修复(必须)

  1. runHTTPTool 中添加 isInternalAddress 验证
  2. 修改 CHECK_INTERNAL_IP 默认行为为启用
  3. 添加单元测试验证修复

第二阶段:文档更新(必须)

  1. 更新部署文档,说明 CHECK_INTERNAL_IP 环境变量的变化
  2. 添加安全最佳实践文档
  3. 更新 CHANGELOG

第三阶段:增强保护(可选)

  1. 实现 DNS rebinding 保护
  2. 添加请求日志和监控
  3. 实现 URL 白名单机制

测试计划

单元测试

创建测试文件: test/cases/service/core/app/http.test.ts

测试用例:

  1. 测试拒绝 AWS 元数据端点 (169.254.169.254)
  2. 测试拒绝 Kubernetes 服务 (kubernetes.default.svc)
  3. 测试拒绝私有 IP 范围 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  4. 测试拒绝 localhost 和 127.0.0.1
  5. 测试允许合法的外部 URL
  6. 测试 DNS rebinding 场景(域名解析到内部 IP)

集成测试

  1. 测试 HTTP Tool 在工作流中的行为
  2. 测试 API 端点 /api/core/app/httpTools/runTool
  3. 验证错误消息的正确性

向后兼容性

破坏性变更

  1. CHECK_INTERNAL_IP 默认值变更:

    • 旧行为: 默认允许内部地址访问
    • 新行为: 默认拒绝内部地址访问
  2. 影响范围:

    • 依赖访问内部服务的工作流将失败
    • 需要显式设置 CHECK_INTERNAL_IP=false 来恢复旧行为(不推荐)

迁移指南

对于需要访问内部服务的合法用例:

  1. 推荐方案: 使用代理服务或 API 网关
  2. 临时方案: 设置 CHECK_INTERNAL_IP=false(不安全,仅用于开发环境)

安全建议

  1. 生产环境: 始终保持 CHECK_INTERNAL_IP=true(默认)
  2. 网络隔离: 在网络层面限制 FastGPT 服务器的出站访问
  3. 监控: 记录所有 HTTP Tool 请求,监控异常模式
  4. 最小权限: 限制 FastGPT 服务账号的权限

参考资料