Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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