[P0/严重] HttpRequestTool 未防御 SSRF,可访问内网与云元数据服务 #10

Closed
opened 2026-07-21 21:55:41 +08:00 by thzxx · 1 comment
Owner

问题类型

安全漏洞 / 严重 / 工具系统

文件位置

electron/harness/tools/built-in/http-request.ts

问题描述

HttpRequestTool 接受任意 URL,未做 SSRF 防护。LLM 可调用:

  • http://169.254.169.254/latest/meta-data/ (AWS / GCP / Azure 元数据,可获取云凭证)
  • http://localhost:port/ (访问本机内部服务,如数据库、Redis、管理 API)
  • http://10.0.0.0/8 / http://192.168.0.0/16 (内网扫描)
  • http://[::1]/ (IPv6 回环)

这违反了 OWASP SSRF 防御最佳实践。

影响

  • 云凭证泄露(IAM 临时凭证可被用于接管云账户)
  • 内网服务暴露
  • 横向移动跳板

建议修复

  1. URL 解析与 IP 校验
import { lookup } from 'dns/promises';
import { isIP } from 'net';

async function isPrivateIP(ip: string): Promise<boolean> {
  // 检查 IPv4 私有段 / 回环 / 链路本地 / 元数据
  const privateRanges = [
    /^127\./, /^10\./, /^192\.168\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
    /^169\.254\./, /^0\./, /^224\./, /^240\./,
  ];
  if (privateRanges.some(re => re.test(ip))) return true;
  // IPv6
  if (ip === '::1' || ip.startsWith('fe80::') || ip.startsWith('fc00::')) return true;
  return false;
}

// 解析域名后校验 IP
const ips = await lookup(hostname, { all: true });
for (const { address } of ips) {
  if (await isPrivateIP(address)) {
    throw new Error(`Blocked SSRF: ${hostname} resolves to private IP ${address}`);
  }
}
  1. 禁用 redirect 跟随或重新校验重定向后的 URL
  2. 协议白名单:仅允许 http/https
## 问题类型 安全漏洞 / 严重 / 工具系统 ## 文件位置 `electron/harness/tools/built-in/http-request.ts` ## 问题描述 HttpRequestTool 接受任意 URL,未做 SSRF 防护。LLM 可调用: - `http://169.254.169.254/latest/meta-data/` (AWS / GCP / Azure 元数据,可获取云凭证) - `http://localhost:port/` (访问本机内部服务,如数据库、Redis、管理 API) - `http://10.0.0.0/8` / `http://192.168.0.0/16` (内网扫描) - `http://[::1]/` (IPv6 回环) 这违反了 OWASP SSRF 防御最佳实践。 ## 影响 - 云凭证泄露(IAM 临时凭证可被用于接管云账户) - 内网服务暴露 - 横向移动跳板 ## 建议修复 1. **URL 解析与 IP 校验**: ```ts import { lookup } from 'dns/promises'; import { isIP } from 'net'; async function isPrivateIP(ip: string): Promise<boolean> { // 检查 IPv4 私有段 / 回环 / 链路本地 / 元数据 const privateRanges = [ /^127\./, /^10\./, /^192\.168\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./, /^169\.254\./, /^0\./, /^224\./, /^240\./, ]; if (privateRanges.some(re => re.test(ip))) return true; // IPv6 if (ip === '::1' || ip.startsWith('fe80::') || ip.startsWith('fc00::')) return true; return false; } // 解析域名后校验 IP const ips = await lookup(hostname, { all: true }); for (const { address } of ips) { if (await isPrivateIP(address)) { throw new Error(`Blocked SSRF: ${hostname} resolves to private IP ${address}`); } } ``` 2. **禁用 redirect 跟随**或重新校验重定向后的 URL 3. **协议白名单**:仅允许 http/https
thzxx added the ???????????? labels 2026-07-21 21:55:41 +08:00
Author
Owner

修复说明

文件: electron/harness/tools/built-in/http-request.ts

问题: HttpRequestTool 接受任意 URL,无 SSRF 防护,可访问 http://169.254.169.254/(云元数据)、http://localhost/(内部服务)、http://10.0.0.0/8(内网扫描)等。

修复方案:

  1. 新增 isPrivateIP(ip) 函数:检测 IPv4/IPv6 私有地址段
    • IPv4: 127.0.0.0/8、10.0.0.0/8、192.168.0.0/16、172.16.0.0/12、169.254.0.0/16(含云元数据)、0.0.0.0/8、224.0.0.0/4、240.0.0.0/4
    • IPv6: ::1、fe80::/10、fc00::/7、::ffff: 映射的 IPv4
  2. 新增 validateSSRF(url) 函数:
    • 协议白名单:仅允许 http/https
    • DNS 解析域名,获取所有 IP 地址(防止 DNS rebinding)
    • 逐个检测 IP,任意一个为私有即拒绝
  3. execute 方法在 fetch 前调用 validateSSRF
  4. redirect: 'follow' 改为 redirect: 'manual'(防止重定向到内网地址绕过校验)
  5. riskLevel 从 LOW 改为 MEDIUM,requiresPermission 从 false 改为 true

验证: tsc --noEmit 类型检查通过。

## 修复说明 **文件**: `electron/harness/tools/built-in/http-request.ts` **问题**: HttpRequestTool 接受任意 URL,无 SSRF 防护,可访问 `http://169.254.169.254/`(云元数据)、`http://localhost/`(内部服务)、`http://10.0.0.0/8`(内网扫描)等。 **修复方案**: 1. 新增 `isPrivateIP(ip)` 函数:检测 IPv4/IPv6 私有地址段 - IPv4: 127.0.0.0/8、10.0.0.0/8、192.168.0.0/16、172.16.0.0/12、169.254.0.0/16(含云元数据)、0.0.0.0/8、224.0.0.0/4、240.0.0.0/4 - IPv6: ::1、fe80::/10、fc00::/7、::ffff: 映射的 IPv4 2. 新增 `validateSSRF(url)` 函数: - 协议白名单:仅允许 http/https - DNS 解析域名,获取**所有** IP 地址(防止 DNS rebinding) - 逐个检测 IP,任意一个为私有即拒绝 3. `execute` 方法在 fetch 前调用 `validateSSRF` 4. `redirect: 'follow'` 改为 `redirect: 'manual'`(防止重定向到内网地址绕过校验) 5. `riskLevel` 从 LOW 改为 MEDIUM,`requiresPermission` 从 false 改为 true **验证**: `tsc --noEmit` 类型检查通过。
thzxx closed this issue 2026-07-22 09:24:46 +08:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MetonaTeam/metona-ai-desktop#10