背景
PR #295 给 Next.js 的 `/api/chat` 和 `/api/suggestions` 加了 Upstash per-IP rate-limit。但登录用户可以绕过整个 Next.js 层,直接 curl 后端:
```bash
curl -X POST https:///openai/responses/stream \
-H "satoken: " \
-H "Content-Type: application/json" \
-d '{...}'
```
- 命中的是 `OpenAiStreamController.streamResponses`,有 `@SaCheckLogin` 但没有任何 rate-limit
- Java 那边烧的是 `OPENAI_API_KEY`(付费),不是前端的 GLM 免费额度
- Caddy 是裸 `reverse_proxy` 透传,不过滤路径也不限流
影响
- 风险级别:中(要先登录+知道 URL+主动绕,门槛不低)
- 潜在损失:如果一个恶意用户循环调用 10 tokens 的 prompt × 10 次/秒,每小时烧 ~$5 OpenAI 额度
修复路径(三选一,按难度)
A. Java 层加分布式 rate-limit(推荐)
- 在 `OpenAiStreamController` 前加 Spring Interceptor / AOP
- 用和 Next.js 同一个 Upstash Redis 实例,共享额度桶(key = userId,不是 IP)
- 每用户 20 req/min 就够拦住滥用,正常用户完全感知不到
B. Caddy 层 per-IP rate-limit
- Caddy 原生支持 `rate_limit` 插件
- 比 A 粗糙:打在 IP 级别,同 NAT 多用户会被误伤
- 优点:零代码,改 Caddyfile 就行
C. Next.js ↔ Java 共享密钥,Java 只认内网请求
- Next.js 代理时带一个 `X-Internal-Token` header
- Java 的 `/openai/responses/stream` 加 Interceptor 校验该 header
- 外部直连全部 403
- 最彻底,但需要前后端两边改
建议
先做 A。@Crokily 你看呢?Issue 先挂着,等这波 PR 全合完再排日程。
Refs: #295 #296
背景
PR #295 给 Next.js 的 `/api/chat` 和 `/api/suggestions` 加了 Upstash per-IP rate-limit。但登录用户可以绕过整个 Next.js 层,直接 curl 后端:
```bash
curl -X POST https:///openai/responses/stream \
-H "satoken: " \
-H "Content-Type: application/json" \
-d '{...}'
```
影响
修复路径(三选一,按难度)
A. Java 层加分布式 rate-limit(推荐)
B. Caddy 层 per-IP rate-limit
C. Next.js ↔ Java 共享密钥,Java 只认内网请求
建议
先做 A。@Crokily 你看呢?Issue 先挂着,等这波 PR 全合完再排日程。
Refs: #295 #296