fix: 过滤 Cloudflare CDN headers 以防止 API 安全检查

使用 Cloudflare 橙色云(CDN 代理模式)时,Cloudflare 会自动添加 CDN 相关的 headers
(cf-*, x-forwarded-*, cdn-loop 等),这会触发上游 API 提供商的安全检查:

1. 已确认问题:88code API 检测到 CDN headers 后返回 403 Forbidden,
   导致 Codex CLI 无法使用
2. 潜在风险:其他 API 提供商(OpenAI、Anthropic)可能也会因检测到
   代理/CDN 特征而采取限制措施

创建统一的 headerFilter 工具类,在所有转发服务中过滤 Cloudflare CDN headers,
使转发请求伪装成正常的直接客户端请求。

1. 新增 src/utils/headerFilter.js
   - 统一的 CDN headers 过滤列表(13 个 Cloudflare headers)
   - 提供 filterForOpenAI() 和 filterForClaude() 方法
   - 在现有过滤逻辑基础上添加 CDN header 过滤

2. 更新 src/services/openaiResponsesRelayService.js
   - 使用 filterForOpenAI() 替代内联的 _filterRequestHeaders()
   - 保持向后兼容性

3. 更新 src/services/claudeRelayService.js
   - 使用 filterForClaude() 替代 _filterClientHeaders() 实现
   - 简化代码,移除重复的 header 列表定义

4. 修复 src/routes/openaiRoutes.js
   - 添加对 input 字段的类型检查(可以是数组或字符串)
   - 防止 "startsWith is not a function" 错误

x-real-ip, x-forwarded-for, x-forwarded-proto, x-forwarded-host,
x-forwarded-port, x-accel-buffering, cf-ray, cf-connecting-ip,
cf-ipcountry, cf-visitor, cf-request-id, cdn-loop, true-client-ip

-  Codex CLI 通过中转服务成功调用 88code API(之前返回 403)
-  保留所有业务必需的 headers(conversation_id、session_id 等)
-  移除所有 Cloudflare CDN 痕迹
-  保持橙色云的 DDoS 防护和 CDN 加速优势
-  Docker 构建成功

1. 解决 88code 403 问题,Codex CLI 可正常使用
2. 降低因 CDN/代理特征被上游 API 识别的风险
3. 提升与各种 API 提供商的兼容性
4. 统一管理 CDN headers 过滤逻辑,便于维护
This commit is contained in:
IanShaw027
2025-12-03 06:33:50 -08:00
parent 279cd72f23
commit 5fd78b6411
3 changed files with 143 additions and 80 deletions

View File

@@ -1,6 +1,7 @@
const axios = require('axios')
const ProxyHelper = require('../utils/proxyHelper')
const logger = require('../utils/logger')
const { filterForOpenAI } = require('../utils/headerFilter')
const openaiResponsesAccountService = require('./openaiResponsesAccountService')
const apiKeyService = require('./apiKeyService')
const unifiedOpenAIScheduler = require('./unifiedOpenAIScheduler')
@@ -73,9 +74,9 @@ class OpenAIResponsesRelayService {
const targetUrl = `${fullAccount.baseApi}${req.path}`
logger.info(`🎯 Forwarding to: ${targetUrl}`)
// 构建请求头
// 构建请求头 - 使用统一的 headerFilter 移除 CDN headers
const headers = {
...this._filterRequestHeaders(req.headers),
...filterForOpenAI(req.headers),
Authorization: `Bearer ${fullAccount.apiKey}`,
'Content-Type': 'application/json'
}
@@ -810,29 +811,10 @@ class OpenAIResponsesRelayService {
return { resetsInSeconds, errorData }
}
// 过滤请求头
// 过滤请求头 - 已迁移到 headerFilter 工具类
// 此方法保留用于向后兼容,实际使用 filterForOpenAI()
_filterRequestHeaders(headers) {
const filtered = {}
const skipHeaders = [
'host',
'content-length',
'authorization',
'x-api-key',
'x-cr-api-key',
'connection',
'upgrade',
'sec-websocket-key',
'sec-websocket-version',
'sec-websocket-extensions'
]
for (const [key, value] of Object.entries(headers)) {
if (!skipHeaders.includes(key.toLowerCase())) {
filtered[key] = value
}
}
return filtered
return filterForOpenAI(headers)
}
// 估算费用(简化版本,实际应该根据不同的定价模型)