Skip to content

[security] Java /openai/responses/stream 绕过 Next.js 限流,登录用户可直烧 OpenAI 付费额度 #297

@longsizhuo

Description

@longsizhuo

背景

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions