Files
netdisk-fast-download/parser/doc/security/SSRF_PROTECTION.md
2025-11-29 03:41:51 +08:00

7.6 KiB
Raw Blame History

SSRF防护策略说明

🛡️ 当前防护策略(已优化)

为了保证功能可用性和安全性的平衡SSRF防护策略已调整为宽松模式,只拦截明确的危险请求。


允许的请求

以下请求不会被拦截,可以正常使用:

1. 外网域名

http.get('https://www.example.com/api/data')  // ✅ 允许
http.get('http://api.github.com/repos')       // ✅ 允许
http.get('https://cdn.jsdelivr.net/file.js')  // ✅ 允许

2. 公网IP

http.get('http://8.8.8.8/api')               // ✅ 允许公网IP
http.get('https://1.1.1.1/dns-query')        // ✅ 允许Cloudflare DNS

3. DNS解析失败的域名

// 即使DNS暂时无法解析也允许继续
http.get('http://some-new-domain.com')       // ✅ 允许DNS失败不拦截

拦截的请求

以下请求会被拦截,保护服务器安全:

1. 本地回环地址

http.get('http://127.0.0.1:8080/admin')      // ❌ 拦截
http.get('http://localhost/secret')          // ❌ 拦截解析到127.0.0.1
http.get('http://[::1]/api')                 // ❌ 拦截IPv6本地

2. 内网IP地址

http.get('http://192.168.1.1/config')        // ❌ 拦截内网C类
http.get('http://10.0.0.5/admin')            // ❌ 拦截内网A类
http.get('http://172.16.0.1/api')            // ❌ 拦截内网B类

3. 云服务元数据API

http.get('http://169.254.169.254/latest/meta-data/')           // ❌ 拦截AWS/阿里云)
http.get('http://metadata.google.internal/computeMetadata/')   // ❌ 拦截GCP
http.get('http://100.100.100.200/latest/meta-data/')          // ❌ 拦截(阿里云)

4. 解析到内网的域名

// 如果域名DNS解析指向内网IP会被拦截
http.get('http://internal.company.com')      // ❌ 拦截如果解析到192.168.x.x

🔍 检测逻辑

防护流程

用户请求 URL
    ↓
1. 检查是否为云服务元数据API域名
    ├─ 是 → ❌ 拦截
    └─ 否 → 继续
    ↓
2. 检查Host是否为IP地址格式
    ├─ 是 → 检查是否为内网IP
    │       ├─ 是 → ❌ 拦截
    │       └─ 否 → ✅ 允许
    └─ 否(域名)→ 继续
    ↓
3. 尝试DNS解析域名
    ├─ 解析成功
    │   ├─ IP为内网 → ❌ 拦截
    │   └─ IP为公网 → ✅ 允许
    └─ 解析失败 → ✅ 允许(不阻止)

内网IP判断规则

使用正则表达式匹配:

^(127\..*|                    // 127.0.0.0/8    - 本地回环
  10\..*|                     // 10.0.0.0/8     - 内网A类
  172\.(1[6-9]|2[0-9]|3[01])\..*|  // 172.16.0.0/12  - 内网B类
  192\.168\..*|               // 192.168.0.0/16 - 内网C类
  169\.254\..*|               // 169.254.0.0/16 - 链路本地
  ::1|                        // IPv6本地回环
  [fF][cCdD].*)               // IPv6唯一本地地址

📊 策略对比

场景 严格模式(原版) 宽松模式(当前)
外网域名 可能被拦截 允许
DNS解析失败 被拦截 允许
公网IP 允许 允许
内网IP 拦截 拦截
本地回环 拦截 拦截
云服务元数据 拦截 拦截
解析到内网的域名 拦截 拦截

🧪 测试用例

测试1: 正常外网请求

function parse(shareLinkInfo, http, logger) {
    try {
        var response = http.get('https://httpbin.org/get');
        logger.info('✅ 成功访问外网: ' + response.substring(0, 50));
        return 'SUCCESS';
    } catch (e) {
        logger.error('❌ 外网请求被拦截(不应该): ' + e.message);
        return 'FAILED';
    }
}

期望结果: 成功访问

测试2: 内网攻击拦截

function parse(shareLinkInfo, http, logger) {
    try {
        var response = http.get('http://127.0.0.1:6400/');
        logger.error('❌ 内网访问成功(不应该)');
        return 'SECURITY_BREACH';
    } catch (e) {
        logger.info('✅ 内网访问被拦截: ' + e.message);
        return 'PROTECTED';
    }
}

期望结果: 被拦截,显示"安全拦截: 禁止访问内网IP地址"

测试3: 云服务元数据拦截

function parse(shareLinkInfo, http, logger) {
    try {
        var response = http.get('http://169.254.169.254/latest/meta-data/');
        logger.error('❌ 元数据API访问成功不应该');
        return 'SECURITY_BREACH';
    } catch (e) {
        logger.info('✅ 元数据API被拦截: ' + e.message);
        return 'PROTECTED';
    }
}

期望结果: 被拦截,显示"安全拦截: 禁止访问云服务元数据API"


🎯 安全建议

当前策略适用于

  • 需要访问多种外网API的场景
  • 网盘、文件分享等服务
  • 需要爬取外网资源
  • 对可用性要求较高的环境

⚠️ 如需更严格的防护

如果你的应用场景需要更严格的安全控制,可以考虑:

1. 白名单模式

只允许访问特定域名:

private static final String[] ALLOWED_DOMAINS = {
    "api.example.com",
    "cdn.example.com"
};

private void validateUrlSecurity(String url) {
    String host = new URI(url).getHost();
    boolean allowed = false;
    for (String domain : ALLOWED_DOMAINS) {
        if (host.equals(domain) || host.endsWith("." + domain)) {
            allowed = true;
            break;
        }
    }
    if (!allowed) {
        throw new SecurityException("域名不在白名单中");
    }
}

2. 协议限制

只允许HTTPS

String scheme = uri.getScheme();
if (!"https".equalsIgnoreCase(scheme)) {
    throw new SecurityException("仅允许HTTPS协议");
}

3. 端口限制

只允许标准端口80, 443

int port = uri.getPort();
if (port != -1 && port != 80 && port != 443) {
    throw new SecurityException("仅允许标准HTTP/HTTPS端口");
}

📝 配置说明

修改黑名单

JsHttpClient.java 中修改:

// 危险域名黑名单
private static final String[] DANGEROUS_HOSTS = {
    "localhost",
    "169.254.169.254",           // AWS/阿里云元数据
    "metadata.google.internal",  // GCP元数据
    "100.100.100.200",          // 阿里云元数据
    // 添加更多...
};

修改内网IP规则

// 内网IP正则表达式
private static final Pattern PRIVATE_IP_PATTERN = Pattern.compile(
    "^(127\\..*|10\\..*|172\\.(1[6-9]|2[0-9]|3[01])\\..*|192\\.168\\..*|169\\.254\\..*|::1|[fF][cCdD].*)"
);

🔄 策略变更历史

v2 - 宽松模式(当前)

  • 日期: 2025-11-29
  • 变更:
    • DNS解析失败不拦截
    • URL格式错误不拦截
    • 只拦截明确的内网攻击
  • 原因: 避免误杀正常外网请求

v1 - 严格模式

  • 日期: 2025-11-28
  • 变更: 初始实现
  • 问题: 过于严格,导致很多正常请求被拦截

📞 反馈

如果遇到以下情况,请考虑调整策略:

  1. 正常外网请求被拦截 → 检查DNS解析、域名是否在黑名单
  2. 内网攻击未被拦截 → 添加更多内网IP段或域名黑名单
  3. 性能问题 → 考虑缓存DNS解析结果

最后更新: 2025-11-29
当前版本: v2 - 宽松模式
安全级别: ⚠️ 中等(建议生产环境根据实际需求调整)