镜像自 github.com/Chieko3020/chat-api
  • JavaScript 100%
Find a file
Chieko3020 f447c8eb45 security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核
- 修绕过:origin.includes(domain) 是子串匹配,https://chieko3020.xyz.evil.com
  一类来源可通过校验;改为解析 hostname 后精确或子域名匹配(server.js 的
  CORS 同款问题一并修,且不再对无 Origin 请求回 *)
- auth.js 显式限定 JWT 算法为 HS256
- 内容过滤分层:语义敏感词移出源码改为可配,保留确定性技术模式
- 输出侧审核:DeepSeek 无 moderation 端点,用一次 flash 判别调用实现,
  默认 fail-open
2026-09-30 06:05:04 +08:00
config/prompts refactor: deepseek.js prompt 从文件加载, 模型名 DEEPSEEK_MODEL 环境变量 2026-07-30 16:20:08 +08:00
middleware security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00
public Initial commit 2026-07-25 16:12:28 +08:00
services security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00
test security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00
.env.example security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00
.gitignore docs: .gitignore 2026-07-30 16:29:36 +08:00
chat-api.service Initial commit 2026-07-25 16:12:28 +08:00
DEPLOY.md Initial commit 2026-07-25 16:12:28 +08:00
package-lock.json Initial commit 2026-07-25 16:12:28 +08:00
package.json Initial commit 2026-07-25 16:12:28 +08:00
README.md security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00
server.js security: 修 Origin 子串匹配绕过,内容过滤分层,新增输出侧审核 2026-09-30 06:05:04 +08:00

Chat API — 基于 DeepSeek 的博客 AI 聊天服务

  • 为 Hexo 博客提供 AI 角色聊天功能,支持自定义 prompt
  • Express.js + OpenAI SDK,通过 JWT + 严格 Origin/Referer 校验双重鉴权保护 API
  • 分层安全过滤:确定性模式硬拦 + 语义敏感词运行时可配 + URL 移除 + 输入/输出长度限制 + 每日 50K token 配额
  • 请求体限 2KB,速率限制每 IP 每 5 分钟 20 次聊天请求
  • 通过 nginx 反代暴露,systemd 管理,Supervisor 备选
  • 开发环境:Ubuntu 24.04 LTS, Node.js 22, Express 4

项目简介

博客的 Live2D 看板娘接入 AI 聊天功能。用户通过前端组件发起对话,请求经 nginx 反代到 chat-api,chat-api 调用 DeepSeek API 生成角色回复并返回。支持独立的 system prompt 配置。

服务部署在 127.0.0.1:3002,由 nginx 通过 proxy_pass 暴露为 /api/chat。

功能特性

核心功能

  • 角色聊天: 三月七(march7th)和长夜月(evernight)两种人格(示例配置),独立 system prompt
  • JWT 鉴权: GET /api/token 签发 30 分钟 Token,POST /api/chat 验证 Token
  • Origin/Referer 严格校验: 解析 hostname 后精确或子域名匹配,仅允许配置的来源访问,防跨站滥用

安全特性

分层设计——入口真实性是唯一真正有效的一层,其余为纵深防御:

① 入口真实性  严格 host 匹配(Origin 优先,Referer 回退)+ JWT(HS256, IP 绑定, 30min)
② CORE_PATTERNS    确定性技术模式:提示注入 / 危险命令 / 脚本注入 / 凭证探测
③ SEMANTIC_PATTERNS 语义敏感词:默认空,经 SENSITIVE_PATTERNS 或 config/sensitive.json 注入
④ 输出审核    LLM as judge:回复发出前过一次 flash 判别(可关,fail-open)
⑤ 成本封顶    express-rate-limit(每 IP 5min/20 次)+ 50K token 日配额
⑥ 响应头      nosniff / X-Frame-Options DENY / Referrer-Policy
  • Origin 严格匹配:解析 hostname 后做精确或子域名匹配。注意:早期实现用 origin.includes(domain) 子串匹配,可被 https://chieko3020.xyz.evil.com、 https://notchieko3020.xyz、https://evil.com/?x=chieko3020.xyz 绕过,已修复并有回归测试。
  • 输入过滤:仅对确定性技术模式硬拦;语义敏感词不写死在源码(正则拦不住变形/谐音/多轮绕过, 却会误伤正常技术讨论),改为运行时可配。
  • 输出审核:DeepSeek 没有 moderation 端点(只有服务端内联过滤,违规返回 400), 因此用一次 flash 判别调用实现。默认 fail-open —— 审核不可用时不阻塞聊天。 ⚠️ 不要指向本机 ai-gateway:它是语义缓存网关,会把"相似" prompt 判为同一请求并复用答案, 实测出现过"违规样本命中缓存的 allow=true 而放行"。
  • 输出审计:URL 移除、600 字符截断、异常回复兜底。
  • 速率限制:每 IP 每 5 分钟 20 次聊天,Token 端点 30 次。
  • 每日配额:50K token/天,由 Python daily_counter 跨服务共享。
  • 请求体限:express.json({ limit: "2kb" })。

测试

node test/test.js              # 25 用例:token/鉴权/过滤/安全头/Origin 绕过回归/分层过滤/JWT 算法
node test/test_moderation.js   # 17 用例:verdict 解析/故障降级/真实判别能力(需 API key)

测试

node test/test.js      # 25 用例:token/鉴权/过滤/安全头/Origin 绕过回归/分层过滤/JWT 算法

技术栈

组件 选型
运行时 Node.js 22
Web 框架 Express 4
LLM SDK openai (OpenAI 兼容)
鉴权 jsonwebtoken
频控 express-rate-limit
LLM 后端 DeepSeek v4-flash

项目结构

chat-api/
├── server.js                 # Express 入口(路由 + 中间件)
├── services/
│   └── deepseek.js           # LLM API 客户端 + 角色 system prompt
├── middleware/
│   ├── auth.js               # JWT 签发与验证
│   └── security.js            # 安全过滤/Referer/频控/配额
├── config/
│   └── prompts/
│       ├── march7th.txt      # 三月七 system prompt
│       └── evernight.txt     # 长夜月 system prompt
├── public/
│   └── waifu-chat.js         # 前端聊天组件
├── .env / .env.example       # 环境变量
├── chat-api.service          # systemd 服务
├── DEPLOY.md                 # 部署文档
└── package.json

架构设计

Hexo 博客 (nginx :80/:443)
    │  POST /api/chat
    ▼
nginx (127.0.0.1:3002)
    │
    ▼
┌───────────────────────────────────┐
│  Express (3002)                   │
│  securityHeaders → json(2kb) →    │
│  rateLimit → referer → dailyQuota │
│  → auth → inputFilter → handler   │
│                                   │
│  handler:                         │
│    deepseek.chat(message, model)  │
│    ├─ march7th                    │
│    └─ evernight                   │
│                                   │
│  output:                          │
│    sanitizeOutput → response      │
└───────────────────────────────────┘
    │
    ▼
DeepSeek API (deepseek-v4-flash)

编译和运行

依赖

cd ~/chat-api
npm install

配置

cp .env.example .env
# 编辑 .env 填入 DEEPSEEK_API_KEY 和 JWT_SECRET

启动

node server.js
# Listening on http://127.0.0.1:3002

systemd 部署

sudo cp chat-api.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now chat-api

配置项

环境变量 默认 说明
PORT 3002 监听端口
DEEPSEEK_API_KEY — DeepSeek API Key
DEEPSEEK_BASE_URL api.deepseek.com/v1 API 地址
DEEPSEEK_MODEL deepseek-v4-flash 模型名称
JWT_SECRET — JWT 签名密钥
ALLOWED_ORIGINS chieko3020.xyz 允许的来源域名(逗号分隔)。严格 host 匹配,非子串包含
SENSITIVE_PATTERNS (空) 语义敏感词兜底,逗号分隔的正则片段,默认不启用
MODERATION_ENABLED 1 输出审核开关
MODERATION_URL 官方 chat/completions 审核端点。勿指向 ai-gateway(语义缓存会串答案)
MODERATION_MODEL deepseek-flash 审核模型
MODERATION_TIMEOUT_SEC 8 审核超时秒数
MODERATION_MAX_TOKENS 256 审核输出上限(推理模型需留余量)
MODERATION_FAIL_MODE open open 放行 / closed 拦截

API

GET /api/token

获取 JWT 访问令牌(30 分钟有效)

POST /api/chat

{ "message": "你好", "history": [], "model": "march7th" }
→ { "reply": "你好呀~", "model": "march7th" }

GET /ping

健康检查