Closed
InvolutionHell/involutionhell-backend
#41Description
背景
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 全合完再排日程。
Metadata
Metadata
Assignees
Labels
No labels
Activity
longsizhuo commentedon Apr 16, 2026
更新:后端现状摸清 + 风险重新评估
登录服务器直接查了运行中的后端容器,拿到真实情况:
```
OPENAI_API_KEY = <过期的 Intern-S1 key> ← 不是真 OpenAI key
OPENAI_API_URL = https://api.openai.com/v1 ← 但 URL 指的是真 OpenAI
OPENAI_MODEL = gpt-4.1
```
结论:Java 这条路径当前完全没跑通。每次请求 → OpenAI 返 401 → Java 抛错 → Next.js fallback 到本地 GLM。登录用户和匿名用户的 AI 回答都来自 Next.js 的 GLM。
这意味着:
处理方案(按 @longsizhuo 决策)
Java 路径保留作为 fallback,但底层也走 GLM(免费 ≈ 无烧钱风险):
.env.example和docker-compose.yml默认值 OpenAI → GLMOPENAI_*,Java 代码走 OpenAI-compatible 协议不动一行本 Issue 状态
降级为"跟踪项"。当且仅当未来真激活 OpenAI 付费时才需要做 Java 层的 per-user rate-limit(方案 A)。到时再 reopen 或新开 issue 跟进。
建议:本 issue 等 backend PR #8 合并后关闭。
longsizhuo commentedon Jul 18, 2026
修复 PR 已开:InvolutionHell/involutionhell-backend#41 —— Java 层每用户限流(Caffeine 固定窗口,默认 10 次/分钟,
OPENAI_STREAM_RPM可调),超限 429;SECURITY.md 立了 INV-006 不变量 + 3 个回归测试防回归。merge 后本 issue 自动关闭。