Skip to content

安全加固:修复未鉴权 API、XSS、路径穿越、SSRF 与默认部署风险 #22

Description

@massif-01

背景

本次安全审计使用子代理并行覆盖了后端/API、前端/插件、依赖/供应链与部署配置。当前决定暂不处理以下两类架构性问题:

  • 插件安装边界过宽、外部插件来源/签名/默认启用策略
  • 插件无 sandbox、插件同源执行模型

本 issue 聚焦修复其余安全问题,优先收住可远程利用、凭据泄露、XSS、SSRF、路径穿越、默认部署暴露面和供应链可复现性。

需要修复的问题

P0:补安全回归测试

先补测试护栏,避免修复后回退。

  • 新增 backend/tests/test_security.py
  • 覆盖未认证访问 /api/models 返回 401
  • 覆盖 /api/models 不返回明文 api_key
  • 覆盖 /fonts/%2e%2e/%2e%2e/data/chatraw.db 返回 400/404
  • 覆盖 /api/parse-url 拒绝 127.0.0.1、localhost、169.254.169.254、192.168.x.x
  • 覆盖 /api/proxy/request 拒绝内网、IPv6 loopback、DNS 解析到私网的 host
  • 覆盖 renderMarkdown('<img src=x onerror=alert(1)>') 输出不含 onerror

P1:认证、CORS、API Key 泄露

目标:默认本机安全,局域网/公网部署必须显式配置。

  • 在 backend/main.py 增加配置:
    • CHATRAW_AUTH_TOKEN
    • AUTH_ENABLED=true
    • BIND_HOST=127.0.0.1
  • 增加 AuthMiddleware
    • 默认保护 /api/*
    • 放行 /health、/ready、静态资源
    • 支持 Authorization: Bearer <token>
  • 修改启动绑定:
    • uvicorn.run(app, host=os.environ.get("BIND_HOST", "127.0.0.1"), ...)
  • 修改 CORS 默认值:
    • 不再默认 CORS_ORIGINS="*"
    • 默认只允许 http://127.0.0.1:51111 和 http://localhost:51111,或要求部署方显式配置
  • 修改 /api/models
    • 默认只返回 api_key_set: true/false 或 masked key
    • 不返回明文 api_key
  • /api/models POST 保存 key 和 /api/models/verify 必须通过认证

P2:修复 /fonts/{path} 路径穿越

目标:任何编码形式的 .. 都不能逃出字体目录。

  • 优先删除手写 /fonts/{path:path} 路由,改用:
    • app.mount("/fonts", StaticFiles(directory="static/fonts"), name="fonts")
  • 如果保留手写路由:
    • 使用 Path("static/fonts").resolve() 作为根目录
    • 对目标路径执行 resolve()
    • 强制 target.relative_to(font_root) 成功
    • 拒绝 ..、绝对路径和非必要字体扩展

已验证风险:/fonts/%2e%2e/%2e%2e/data/chatraw.db 在当前实现下可返回 200。

P3:Markdown XSS 和导出 XSS

目标:所有 Markdown 渲染统一净化。

  • 在前端引入本地 vendored DOMPurify 或等价白名单净化器
  • 修改 backend/static/app.js 的 renderMarkdown(content)
    • marked.parse(content) 的输出必须经过 sanitizer
    • 或禁用 raw HTML,仅保留安全 Markdown 渲染
  • 同步重建 backend/static/app.min.js
  • 修改 Plugins/Plugin_market/enhanced-export/main.js
    • HTML/PDF 导出也走同一套 sanitizer
    • 不允许把未净化 marked.parse() 结果直接写入 innerHTML
  • 修复插件错误字符串拼 innerHTML:
    • Plugins/Plugin_market/bocha-search/main.js
    • Plugins/Plugin_market/tavily-search/main.js
    • Plugins/Plugin_market/web-parser/main.js
    • 变量内容用 textContent 或 escapeHtml()

P4:统一 SSRF Guard

目标:所有服务端出站 URL 走同一套校验。

  • 在 backend/main.py 新增统一 helper:
    • validate_external_url(url, allow_http=False)
    • resolve_hostname(hostname)
    • is_blocked_ip(ip)
  • 阻断:
    • loopback:127.0.0.0/8、::1
    • private:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、fc00::/7
    • link-local:169.254.0.0/16、fe80::/10
    • metadata:169.254.169.254
    • multicast、reserved、unspecified 地址
  • 应用到:
    • /api/fetch-raw-url
    • /api/parse-url
    • /api/proxy/request
    • /api/proxy/upload
    • /api/models/verify
  • 禁止 redirect,或每次 redirect 后重新校验目标 URL

P5:远程 CDN 脚本、SRI、CSP

目标:主应用自身不再依赖无 SRI 的远程脚本。

  • 把以下依赖 vendoring 到 backend/static/vendor/
    • marked
    • Alpine
    • @alpinejs/collapse
    • highlight.js
  • 修改 backend/static/index.html 中的远程脚本引用为本地路径
  • 修改 backend/static/app.js 中 highlight.js lazy load 为本地路径
  • 增加 CSP header,初始建议:
    • default-src 'self'
    • script-src 'self'
    • style-src 'self' 'unsafe-inline'
    • img-src 'self' data: blob:
    • font-src 'self'
    • connect-src 'self' https:
    • frame-ancestors 'self'

说明:本 issue 不处理第三方插件外部依赖的信任模型,但主应用自身应先移除 CDN 运行时依赖。

P6:Docker 默认安全化

目标:默认部署不把服务暴露到整个 host 网络,并降低容器内权限。

  • Dockerfile
    • 固定 Python patch 版本或 digest
    • 创建非 root 用户 chatraw
    • /app/data 归属该用户
    • 使用 USER chatraw
  • docker-compose.yml
    • 默认移除 network_mode: host
    • 改为 ports: ["127.0.0.1:51111:51111"]
    • 如确需 LAN/host 网络访问,另写 docker-compose.host.yml 或 profile
    • 增加 security_opt: ["no-new-privileges:true"]
    • 增加 cap_drop: ["ALL"]

P7:依赖可复现性与 CI 安全扫描

目标:同一 commit 构建出相同依赖图,CI 文案与真实检查一致。

  • Python:
    • 生成 backend/requirements.lock
    • Docker/CI 使用 lock 安装
    • 后续可进一步引入 hash pin
  • npm:
    • 从 .gitignore 删除 package-lock.json
    • 提交 package-lock.json
    • CI 使用 npm ci
  • CI:
    • 补齐真实安全扫描,或删掉 PR 模板中的空承诺
    • 建议至少加入:
      • CodeQL for Python/JavaScript
      • Trivy filesystem/container scan
      • pip-audit
      • npm audit 或 GitHub dependency review

推荐落地顺序

  1. P0:安全回归测试
  2. P1:认证、CORS、API Key 脱敏
  3. P2:字体路径穿越
  4. P4:SSRF guard
  5. P3:Markdown XSS
  6. P6:Docker 默认安全
  7. P5:vendoring/CSP
  8. P7:依赖和 CI

验收标准

  • 默认启动只监听 127.0.0.1
  • 未认证请求无法访问受保护 /api/*
  • /api/models 不返回明文 api_key
  • 编码路径穿越无法读取 data/chatraw.db
  • 内网、metadata、loopback URL 均被服务端出站请求拒绝
  • 聊天消息和导出 HTML/PDF 中的危险 HTML 不执行
  • 主应用运行时不依赖无 SRI 的 CDN 脚本
  • 默认 compose 不使用 host network
  • 依赖安装有锁定文件,CI 与 PR 模板描述一致

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

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions