Files
Cyrene/docs/debug/2026-05-20-round5-security-boundary.md
T

13 KiB
Raw Blame History

Cyrene 第5轮调试报告:安全审计 + 边界条件 + 错误处理

日期2026-05-20 14:39 CST (UTC+8) 轮次:第5轮 测试方法:黑盒渗透测试 (curl + 手动审计) 测试范围Gateway (8080) 全端点


一、测试概况

类别 测试项数 通过 发现问题
认证绕过 9 8 1
SQL注入 6 4 2
XSS 5 0 5
路径遍历 3 3 0
权限提升 1 1 0
速率限制 2 0 2
边界条件 10 5 5
错误处理 8 3 5
合计 44 24 20

二、严重发现 (🔴 Critical)

🔴 SEC-001: 注册/登录端点无速率限制

位置: backend/gateway/internal/router/router.go

描述: 登录 (/auth/login) 和注册 (/auth/register) 端点属于公开路由组,未应用限流中间件。RateLimiter 仅附加在 protected 路由组上。

测试方法:

for i in $(seq 1 20); do
  curl -s -X POST http://localhost:8080/api/v1/auth/login \
    -H 'Content-Type: application/json' \
    -d '{"username":"admin","password":"wrong"}'
done

实际结果: 20次请求全部返回 401,无任何 429 Too Many Requests

预期结果: 应在短时间内(如1秒内超过5次)返回 429 限流响应。

影响: 攻击者可对登录端点进行暴力破解,无限尝试密码组合。理论上 JWT 过期时间为 720 小时(30天),但暴力破解风险依然存在。

严重级别: 🔴 Critical


🔴 SEC-002: 用户名未做危险字符过滤 (Stored XSS + Log注入)

位置: backend/gateway/internal/handler/auth_handler.go

描述: 注册端点接受包含 SQL 注入 payload、XSS payload 的用户名。虽然数据库查询使用了参数化查询($1)防止了实际注入执行,但恶意字符串被原样存储到数据库,并嵌入到 JWT token 的 user_id 字段中。

测试方法:

curl -s -X POST http://localhost:8080/api/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"username":"test'\''; DROP TABLE users; --","password":"Test@123456",
       "email":"sqli@test.com","nickname":"SQLiTest","verify_code":"000000"}'

实际结果: 用户成功注册,user_iduser_test'; DROP TABLE users; --JWT token 中包含此 payload。用户可以使用此用户名正常登录。

影响:

  1. 日志注入:用户名出现在日志中可能破坏日志格式
  2. 存储型 XSS:如果前端任何地方直接渲染 user_id 而未转义
  3. 数据完整性:数据库中存储了恶意构造的用户名
  4. Token 膨胀:JWT 中包含特殊字符

严重级别: 🔴 Critical


🔴 SEC-003: 存储型 XSS — 所有内容端点未对输入做 HTML 转义

位置:

描述: 记忆、知识库、提醒的标题/内容/便签字段接受并存储 HTML/JavaScript 代码。虽然 JSON 响应中使用了 Unicode 转义(如 \u003cscript\u003e),但这依赖 JSON 序列化器的默认行为,后端没有主动做输入验证或输出净化。

测试方法:

# 记忆
curl -X POST /api/v1/memory -H "Authorization: Bearer $TOKEN" \
  -d '{"content":"<img src=x onerror=alert(1)>","category":"xss_test"}'
# 知识库
curl -X POST /api/v1/knowledge/bases -H "Authorization: Bearer $TOKEN" \
  -d '{"name":"<script>alert(1)</script>","description":"XSS test"}'
# 提醒
curl -X POST /api/v1/reminders -H "Authorization: Bearer $TOKEN" \
  -d '{"title":"<b>Test</b>","note":"<script>alert(document.cookie)</script>",...}'

实际结果: 所有 XSS payload 被原样存储到数据库。JSON 响应中由 Go 的 encoding/json 自动转义为 Unicode 形式。

影响: 如果前端渲染这些内容时使用 dangerouslySetInnerHTML 或未做 HTML 转义,将导致存储型 XSS 攻击。即使后端 JSON 有转义,前端可能自行解析。

严重级别: 🔴 Critical (取决于前端实现)


🔴 SEC-004: 文件名 XSS 过滤过于粗暴且不透明

位置: backend/gateway/internal/handler/file_handler.go

描述: 文件上传端点对文件名中的 <script> 标签做了过滤,但过滤方式是静默删除标签字符,导致用户得到的文件名与预期完全不同。

测试方法:

echo "XSS test" > /tmp/xss_test.txt
curl -X POST /api/v1/files/upload -H "Authorization: Bearer $TOKEN" \
  -F "file=@/tmp/xss_test.txt;filename=<script>alert(1)</script>.txt"

实际结果: 文件名被变成 script_.txt<, /, > 字符被移除)。原始文件名信息丢失。没有向用户说明文件名被修改的原因。

影响:

  1. 用户提交 filename=<script>.txt → 得到 script_.txt,造成困惑
  2. 没有告警或说明文件名被清理过
  3. 过滤逻辑不透明

严重级别: 🔴 High (功能性 + 安全)


🔴 SEC-005: 超大内容无大小限制

位置:

描述: 记忆和知识库的 content 字段没有大小限制。测试中成功存储了 100,000 字符的内容。

测试方法:

curl -X POST /api/v1/memory -H "Authorization: Bearer $TOKEN" \
  -d "{\"content\":\"$(python3 -c "print('X'*100000)")\",\"category\":\"big\"}"

实际结果: HTTP 200,100KB 内容成功存储。查询时返回完整内容(响应体超过 99KB)。

影响:

  1. 数据库存储膨胀
  2. API 响应过大可能导致前端卡顿或崩溃
  3. 可被用于 DoS 攻击
  4. 带宽浪费

严重级别: 🔴 Medium-High


三、中等发现 (🟡 Medium)

🟡 SEC-006: JWT Secret 使用弱默认值

位置: backend/gateway/internal/config/config.go

描述: JWT 签名密钥默认为 change-me-in-production,此值在源代码中明文可见。虽然生产环境应通过环境变量覆盖,但开发者可能忘记设置。

当前值: getEnv("JWT_SECRET", "change-me-in-production")

影响: 如果生产部署未覆盖此值,攻击者可伪造任意用户的 JWT token。

严重级别: 🟡 Medium


🟡 SEC-007: 管理员权限判断依赖用户名前缀

位置: backend/gateway/internal/middleware/auth.go

描述: 管理员权限通过检查 user_id 是否以 admin_ 前缀开头来判断,而非使用数据库中的 is_admin 字段。

c.Set(IsAdminKey, strings.HasPrefix(userID, "admin_"))

影响: 如果攻击者能注册一个用户名为 admin_fake 的账户(如果注册开关开启),其 user_id 将变成 user_admin_fake(非管理员)。但如果数据库中有用户 admin_xxx 且能通过其他方式认证... 不过由于 user_id = "user_" + username 的拼接方式,普通用户无法获得 admin_ 前缀。

严重级别: 🟡 Low-Medium (设计缺陷,但实际风险取决于注册是否开放)


🟡 SEC-008: 负数值分页参数未被拒绝

位置: 搜索端点(sessions/messages/search, memory/search

描述: limit=-1offset=-100 被静默接受(HTTP 200),虽然实际返回结果正确,但应返回 400 错误。

测试方法:

curl "$BASE/messages/search?q=test&limit=-1&offset=-100" -H "Authorization: Bearer $TOKEN"
# HTTP 200

影响: 低风险,但不符合健壮 API 设计原则。

严重级别: 🟡 Low-Medium


🟡 SEC-009: 空字节导致 500 内部错误

位置: backend/gateway/internal/handler/auth_handler.go

描述: 发送包含 \u0000 的 JSON 请求体导致服务器返回 {"error":"服务器内部错误"} (500),而非 400 参数无效。

测试方法:

curl -X POST /api/v1/auth/login -H 'Content-Type: application/json' \
  -d '{"username":"test\u0000user","password":"pass\u0000word"}'
# HTTP 500: {"error":"服务器内部错误"}

影响: 攻击者可通过发送包含空字节的请求探测服务器内部行为,也可能导致日志膨胀。

严重级别: 🟡 Medium


🟡 SEC-010: 错误 HTTP 方法返回 404 而非 405

位置: backend/gateway/internal/router/router.go

描述: 对端点使用不支持的 HTTP 方法时返回 404 Not Found 而非 405 Method Not Allowed

端点 正确方法 测试方法 实际 预期
/health GET PATCH 404 405
/health GET POST 404 405
/auth/login POST PUT 404 405

影响: 误导客户端,不利于 API 可发现性。

严重级别: 🟡 Low


四、低风险发现 (🟢 Low)

🟢 SEC-011: 错误消息泄露验证细节

描述: 注册失败时的错误消息包含完整的字段验证信息:

{"error":"请求参数无效: Key: 'Password' Error:Field validation for 'Password' failed on the 'required' tag\n..."}

影响: 暴露了后端使用的验证框架(gin binding)和字段名称。

严重级别: 🟢 Low

🟢 SEC-012: 速率限制仅应用于受保护端点

描述: RateLimiter 配置为 10 req/s, burst 20,但仅通过 protected.Use(rateLimiter.Handler()) 应用于需要认证的路由。公开端点(health, login, register)无限制。

测试: 15次快速连续请求到受保护端点均返回 200,说明 burst=20 在当前测试中未触发限流。需要更高频率的测试。

严重级别: 🟢 Low (限流存在但有盲区)

🟢 SEC-013: 超大搜索查询被接受

描述: 5000个字符的搜索查询被接受并正常处理(HTTP 200)。

严重级别: 🟢 Low


五、通过的安全检查

测试项 结果 说明
无 Token 访问 401 所有受保护端点正确拒绝
空 Authorization 头 401 正确处理
非 Bearer 格式 401 格式验证正确
伪造 Token 401 JWT 签名验证有效
篡改 Token 401 签名不匹配被拒绝
过期 Token 401 过期检测正常
管理员权限隔离 403 普通用户访问 /admin 被拒绝
路径遍历 (../) 404 文件/知识库端点正确阻止
路径遍历 (URL编码) 404 %2F 编码的遍历也被阻止
并发请求 200x10 10个并发请求全部成功,无竞态
不存在的资源 404 正确处理
健康检查公开 200 无需认证即可访问
SQL参数化查询 所有 DB 查询使用 $1 占位符
文件类型白名单 仅允许安全类型

六、发现汇总

ID 类别 标题 严重级别
SEC-001 速率限制 登录/注册端点无限流保护 🔴 Critical
SEC-002 输入验证 用户名缺少危险字符过滤 🔴 Critical
SEC-003 XSS 内容端点未做HTML转义 🔴 Critical
SEC-004 XSS 文件名过滤不透明 🔴 High
SEC-005 边界条件 内容大小无上限 🔴 Medium-High
SEC-006 配置安全 JWT Secret 弱默认值 🟡 Medium
SEC-007 权限模型 管理员判断依赖前缀 🟡 Low-Medium
SEC-008 边界条件 负数分页参数被接受 🟡 Low-Medium
SEC-009 错误处理 空字节导致500错误 🟡 Medium
SEC-010 错误处理 错误方法返回404而非405 🟡 Low
SEC-011 信息泄露 错误消息暴露验证细节 🟢 Low
SEC-012 速率限制 公开端点无速率限制 🟢 Low
SEC-013 边界条件 超大搜索查询被接受 🟢 Low

七、建议优先级修复顺序

  1. SEC-001 — 为公开端点添加速率限制(最快修复,最高影响)
  2. SEC-002 — 用户名正则校验 + 字符白名单
  3. SEC-003 — 输入净化/HTML实体转义中间件
  4. SEC-005 — 添加请求体大小限制和内容长度校验
  5. SEC-006 — 环境变量强制检查(启动时验证非默认值)
  6. SEC-004 — 改进文件名清理逻辑并通知用户
  7. SEC-009 — 空字节检测 + 400 错误
  8. SEC-010 — Gin 路由添加 NoMethod 处理
  9. SEC-007 — 使用数据库 is_admin 字段
  10. SEC-008 — 分页参数范围校验

八、测试环境

  • 目标: http://localhost:8080
  • 服务状态: 全部6个微服务运行正常
    • Gateway (8080)
    • AI-Core (8081)
    • IoT-Debug (8083)
    • Memory (8091)
    • Tool-Engine (8092)
    • Voice (8093)
  • 数据库: PostgreSQL (SSH 隧道连接)
  • 测试账号: user_secaudit_tester (普通用户)
  • 测试时间: 2026-05-20 14:39 - 14:50 CST

报告生成时间: 2026-05-20 14:50 CST 下一轮计划: 修复上述安全问题