镜像自 github.com/Chieko3020/qqbot
Find a file
Chieko3020 ff4567130e security: 补入站 Ed25519 验签,内容过滤分层,新增输出侧审核
- 入站验签:此前 ed25519 只用于应答 op=13,从未校验入站请求,任何人
  伪造 webhook 即可调用全部指令(含 MC 管理指令与 LLM 额度)
- 内容过滤分层:语义敏感词移出源码改为可配,保留确定性技术模式
- 输出侧审核:DeepSeek 无 moderation 端点,用一次 flash 判别调用实现,
  默认 fail-open
2026-09-30 06:05:00 +08:00
src security: 补入站 Ed25519 验签,内容过滤分层,新增输出侧审核 2026-09-30 06:05:00 +08:00
tests security: 补入站 Ed25519 验签,内容过滤分层,新增输出侧审核 2026-09-30 06:05:00 +08:00
.env.example security: 补入站 Ed25519 验签,内容过滤分层,新增输出侧审核 2026-09-30 06:05:00 +08:00
.gitignore chore: gitignore 忽略 .env 密钥文件 2026-08-25 07:34:46 +08:00
README.md security: 补入站 Ed25519 验签,内容过滤分层,新增输出侧审核 2026-09-30 06:05:00 +08:00

QQ Bot — 基于 Webhook 的 QQ 机器人

  • QQ 官方 Webhook 机器人,运行在 127.0.0.1:4001(nginx 反代),接收消息并确定性强过滤后回复
  • 模块化设计:core/chat/mc 包结构
  • 内置 MC 服务器监控(状态/性能/TPS/内存/日志/备份)+ RCON + 自动备份
  • 支持 Luna AI 聊天(DeepSeek,通过 ai-gateway),内联键盘交互
  • 支持音乐搜索播放(Enhanced API + 语音上传)
  • 安全:入站 ed25519 验签(防伪造事件)+ msg_id 去重 + 滑动窗口频控 + 分层输入过滤 + 50K 日配额
  • 凭据全部通过环境变量注入(systemd EnvironmentFile → .env),源码零硬编码
  • 开发环境:Python 3.12

项目简介

QQ Bot 后端服务。接收 QQ 官方 Webhook 回调,先做 ed25519 入站验签,再根据白名单指令分发到对应处理函数。MC 监控通过 RCON + systemctl 实现,零外部脚本依赖。聊天和音乐功能通过本地 API 转发。

技术栈

组件 选型
运行时 Python 3.12
HTTP 服务 http.server (标准库)
签名验证 cryptography (ed25519)
RCON socket + struct (标准库)
LLM DeepSeek v4-flash (通过 ai-gateway)

项目结构

qqbot/
├── src/
│   ├── main.py             #   入口:HTTP Handler + 告警线程
│   ├── daily_counter.py    #   日配额计数器
│   ├── core/
│   │   ├── config.py       #   配置 / 日志 / 频控 / _mc_cfg
│   │   ├── qq_client.py    #   QQ Bot API (token/消息/语音)
│   │   ├── signature.py    #   入站 Ed25519 验签 / op=13 应答
│   │   ├── security.py     #   分层输入过滤 / URL 移除
│   │   └── handlers.py     #   指令分发
│   ├── chat/
│   │   ├── luna.py         #   AI 聊天
│   │   └── music.py        #   音乐搜索播放
│   └── mc/
│       └── server.py       #   MC 监控 / RCON / 备份
├── tests/
│   └── test_inbound_signature.py   # 入站验签黑盒验收(7 项)
├── config/
│   └── bot_config.json     # 指令开关 + MC 服务器配置(不入库)
├── .env.example            # 环境变量模板,复制为 .env 填入凭据
├── README.md
└── .gitignore

安全模型

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

① 入站验签    Ed25519(X-Signature-Ed25519, event_ts + raw_body)  ← 缺它则任何人可伪造事件
② 输入过滤    CORE_PATTERNS:提示注入 / 危险命令 / 脚本注入(确定性模式)
③ 语义敏感词  默认空,经 QQBOT_SEMANTIC_PATTERNS 或 config 注入(可选兜底)
④ 输出审核    LLM as judge:回复发出前过一次 flash 判别(可关,fail-open)
⑤ 成本封顶    滑动窗口频控 + 50K tokens 日配额
⑥ 传输边界    仅监听 127.0.0.1,由 nginx 终结 TLS

要点:

  • 验签必须用原始 body 字节,不能 json.dumps 重新序列化。
  • 私钥由 AppSecret 确定性派生(重复拼接至 ≥32 字节取前 32 字节),与官方文档 golang 示例一致。
  • op=13(回调地址验证)是入站请求但不带签名头,故放在验签之前处理。
  • 时间戳参与签名并设容差(默认 300s),超窗拒绝以抗重放。
  • 验签失败一律 401 丢弃并记 WARN,不进入任何 handler。
  • ④ 输出审核:DeepSeek 没有 moderation 端点(只有服务端内联过滤,违规返回 400), 因此用一次 flash 判别调用实现。默认 fail-open —— 审核不可用时不阻塞聊天。 ⚠️ 不要指向本机 ai-gateway:它是语义缓存网关,会把"相似" prompt 判为同一请求 并复用答案,实测出现过"违规样本命中缓存的 allow=true 而放行"。需要走网关时请单起 一个 cache.enabled=false 的实例。
  • 正则黑名单拦不住变形/谐音/多轮绕过,内容合规不要只靠它;政治类词不进源码。

编译和运行

pip install cryptography
cp .env.example .env        # 填入 QQ_APP_ID / QQ_APP_SECRET
python -m src.main
# Listening on http://127.0.0.1:4001

本地跑入站验签验收(自起副本于 4101 端口,不碰生产):

python3 tests/test_inbound_signature.py     # 期望 7/7 passed

生产环境通过 systemd 启动(EnvironmentFile 指向 .env),nginx 将 Webhook 回调反代到 4001 端口。

配置

环境变量(.env)

变量 必填 说明
QQ_APP_ID 是 QQ 开放平台机器人 AppID
QQ_APP_SECRET 是 QQ 开放平台机器人 AppSecret(入站验签派生密钥 + 换取 access_token)
DEEPSEEK_KEY 否 DeepSeek 访问密钥(走 ai-gateway 时可留空)
LISTEN_HOST 否 监听地址,默认 127.0.0.1
LISTEN_PORT 否 监听端口,默认 4001(nginx 反代指向)
DEEPSEEK_URL 否 LLM 上游地址,默认 http://127.0.0.1:4000/v1/chat/completions
QQBOT_VERIFY_SIGNATURE 否 入站验签开关,默认 1。置 0 会使任何人都能伪造事件
QQBOT_SIGNATURE_TOLERANCE_SEC 否 时间戳容差秒数,默认 300
QQBOT_SEMANTIC_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(默认,放行)或 closed(拦截)
MODERATION_KEY 否 审核专用 key,默认复用 DEEPSEEK_KEY

⚠ 凭据只从环境变量读取,源码不包含任何密钥。.env 已被 .gitignore 忽略,严禁提交。

config/bot_config.json

{
  "commands": { "状态": true, "luna": true, ... },
  "mc_server": {
    "name": "MC Server",
    "service_name": "mcserver",
    "server_dir": "/home/ubuntu/minecraft",
    "rcon_password": ""
  }
}
字段 说明
commands.* 指令开关(true/false)
mc_server.name 服务器显示名称
mc_server.service_name systemd 服务名
mc_server.server_dir 服务器根目录
mc_server.rcon_password RCON 密码(可选,从 server.properties 自动读取)

指令

指令 功能
状态 / 性能 / TPS 服务器运行状态
在线人数 / 内存 资源监控
日志 / 备份 日志和备份管理
luna AI 聊天(例: luna 你好)
音乐 音乐搜索和播放
帮助 完整菜单