WindsurfAPI 安全加固全景:fail-closed 认证、SSRF 防护与 Dashboard 越权防御设计指南
【免费下载链接】WindsurfAPITurn Windsurf / Devin Desktop's 100+ AI models (Claude, GPT, Gemini, DeepSeek, Kimi, GLM, SWE) into OpenAI-, Anthropic- & Gemini-compatible APIs. Zero-dependency self-hosted reverse proxy for Claude Code, Cline & Cursor. 把 Windsurf/Devin 云端 100+ 模型变成三套兼容 API。项目地址: https://gitcode.com/gh_mirrors/wi/WindsurfAPI
WindsurfAPI 是一个零依赖、可自托管的 AI 反向代理,把 Windsurf/Devin 云端 100+ 模型(Claude、GPT、Gemini、DeepSeek、Kimi 等)变成 OpenAI/Anthropic/Gemini 三套兼容 API,供 Claude Code、Cline、Cursor 调用。正因为它是"自托管"的,一旦被暴露到公网或配置不当,泄露的就不只是 API Key,而是你背后所有账号的配额与凭证。本文带你完整理解它的三层安全设计:fail-closed 认证默认拒绝、SSRF/DNS 重绑定防护、Dashboard 越权防御,以及它们各自解决了什么真实攻击场景。
为什么自托管 AI 代理最需要"默认拒绝"?
普通 SaaS 服务出问题影响的是服务商;自托管代理出问题,攻击者能直接操作你的账号池——列举邮箱、添加/删除账号、偷看密钥。WindsurfAPI 的总原则是:
不确定时,拒绝。(fail-closed:失败即关闭)
这意味着"没配置"不再等于"放行",而是等于"401 拒绝"。
第一层:fail-closed API 认证(默认拒绝)
设计要点
- 非 localhost 绑定 + 未设置
API_KEY→ 直接拒绝所有请求,不再"默认放行" - 每个请求必须携带
Authorization: Bearer <key>或x-api-key: <key>,与API_KEY环境变量做恒定时间比较(防时序侧信道) - 拒绝时返回的诊断信息会明确告诉你是"key 不匹配"还是"根本没配 key",帮你快速定位是配置问题还是客户端问题
关键逻辑在 src/server.js 中的 API 路由入口(约 L415-L425):
if (!validateApiKey(extractToken(req))) { return json(res, 401, { error: { message, type: 'auth_error' } }); }配置层在 src/config.js 中:当服务绑定到非本地地址而缺少API_KEY/DASHBOARD_PASSWORD时,行为从"默认允许"切换为 fail-closed,并会在启动时给出警告。
🔑实践建议:只想本机用?把HOST=127.0.0.1绑死;要上公网?必须配置强API_KEY。
第二层:Dashboard 越权防御(运营者级权限)
Dashboard 能管理账号池(加删账号、查看邮箱、揭示密钥),这是运营者操作,不是聊天操作。WindsurfAPI 把它拆成了几道独立关卡,源码集中在 src/dashboard/api.js:
1. 聊天 Key ≠ 运营者密码(权限隔离)
早期版本中,共享的聊天 API Key 可以直接当 Dashboard 密码用——任何拿到聊天 key 的客户端都能管理账号池。现在这个"便利"默认关闭:
| 场景 | 行为 |
|---|---|
仅持聊天 API Key 访问/auth/accounts | 403 拒绝 |
| 想复用 key 当本地密码 | 需显式设置DASHBOARD_ALLOW_API_KEY_AS_PASSWORD=1 |
| 本地无密码想开放 | 需显式设置DASHBOARD_ALLOW_NO_AUTH=1,且仅限验证过的本地客户端 |
2. 防反向代理伪装:可信客户端 IP
一个经典坑:你的服务跑在本机,前面套了 Nginx/OpenResty 反向代理——此时每个请求的 socket 对端都是127.0.0.1,远程用户看起来也是"本地用户"。WindsurfAPI 的解法是只信任可信客户端 IP(dashboardClientIp):配置了TRUST_PROXY_X_FORWARDED_FOR=1时按代理跳数从右往左数出真实客户端 IP;没配置就无法验证客户端,本地便利通道直接关闭。
3. 管理员端点共用暴力破解锁定
/auth/login、/auth/accounts等管理端点与 Dashboard API 共用同一个客户端 IP 锁定桶——连续失败 5 次即封禁,带Retry-After响应头(src/server.js L440-L458)。攻击者无法"在 A 端点猜密码、去 B 端点绕过封禁"。
4. 敏感操作二次认证
"揭示 API Key"这类高危操作要求再次提交密码(body.password或X-Dashboard-Password-Confirm头),即使命中会话级认证也要重新证明身份(confirmReauth,src/dashboard/api.js L331 附近)。
相关测试覆盖见 test/dashboard-auth-fail-closed.test.js、test/dashboard-auth-hardening.test.js 与 test/auth-admin-gate.test.js。
第三层:SSRF 防护与 DNS 重绑定防御
代理服务天然容易踩 SSRF(服务器端请求伪造):用户配置的代理主机名如果被恶意 DNS 解析成内网地址,你的服务就会变成攻击内网的跳板。防护核心在 src/net-safety.js:
1. 私网地址识别要"想到刁钻"
isPrivateIp()不只查 IPv4 的10.x/172.16-31.x/192.168.x,还要处理 IPv6 中内嵌 IPv4 的隧道——这些是真实被验证过的绕过面:
64:ff9b::/96(NAT64)与64:ff9b:1::/482002::/16(6to4)2001:0::/32(Teredo,客户端 v4 还做了取反)::a.b.c.d(v4-compatible,如::127.0.0.1)
任何解析结果落入私网空间 → 抛出ERR_PROXY_PRIVATE_IP拒绝连接。回归测试见 test/net-safety-embedded-ipv4.test.js 与 test/ssrf.test.js。
2. 关闭 TOCTOU / DNS 重绑定窗口
检查时解析一次、连接时再解析一次,是经典漏洞:第一次 DNS 返回公网 IP 骗过检查,第二次返回内网 IP。WindsurfAPI 的解法(resolveProxyConnectHost)是校验后直接拨号返回的 IP 字面量——socket 不再做任何二次解析,"校验过的地址"就是"拨号的地址"。并且只要任意一条 A 记录是私网(重绑定应答常混发公网+私网记录),整体拒绝。
3. XFF 头不可信,防锁定绕过
X-Forwarded-For由攻击者随意伪造。WindsurfAPI 的trustedClientIp()默认完全忽略该头;只有配置TRUST_PROXY_X_FORWARDED_FOR=1且按TRUST_PROXY_HOPS(默认 1)从右侧数够跳数时才采信,否则回退到 socket 对端地址。这让暴力破解封禁、按调用方分桶等机制都无法被伪造头绕过(test/caller-key-xff-spoof.test.js 覆盖此回归)。
其他值得注意的加固细节
- 日志脱敏:邮箱打码(
a@b.com → a***@b.com)、凭证不落日志,见 src/log-safety.js 与 test/log-safety.test.js - 原子写盘:账号池写入用
writeFileSyncDurable/renameSyncWithRetry防写坏,见 src/fs-atomic.js - 恒定时间比较:key/密码校验使用
safeEqualString防时序攻击 - 环境变量全景:所有安全开关(含只在源码里存在的 84 个)都有索引,见 docs/ENV-SWITCHES.md
发现漏洞怎么报?
项目提供了正式的私披露通道,72 小时内首次响应,详见 SECURITY.md。范围涵盖认证绕过、凭证泄露、RCE、SSRF、路径穿越、Dashboard API 漏洞等——请勿开公开 issue,那会在修复落地前暴露所有部署实例。
总结:三句话记住这套安全模型
- 认证层:没配置 = 拒绝。聊天 Key 和运营者权限严格分离,敏感操作要二次认证,暴力破解全端点统一锁定。
- 网络层:私网判断覆盖 IPv6 隧道全家桶,DNS 重绑定靠"校验即拨号"的 IP 字面量彻底封死。
- 身份层:XFF 默认不信,本地便利通道必须显式 opt-in,反代场景下远程用户装不成"本地人"。
这套设计对新手最实用的启示是:自托管安全的第一性原理是默认拒绝 + 显式 opt-in,把每一个"图方便"的通道都变成需要主动打开的开关。
【免费下载链接】WindsurfAPITurn Windsurf / Devin Desktop's 100+ AI models (Claude, GPT, Gemini, DeepSeek, Kimi, GLM, SWE) into OpenAI-, Anthropic- & Gemini-compatible APIs. Zero-dependency self-hosted reverse proxy for Claude Code, Cline & Cursor. 把 Windsurf/Devin 云端 100+ 模型变成三套兼容 API。项目地址: https://gitcode.com/gh_mirrors/wi/WindsurfAPI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考