Files
ephron-ren-prd/2026-05-16-ephron-security-audit-prd.md

325 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ephron.ren 安全审计总 PRD2026-05-16 汇总版)
## 文档目的
本 PRD 汇总 2026-05-16 对 `ephron.ren` 进行的多轮安全审计结果,统一记录:
- 已确认安全基线
- 已确认漏洞 / 缺陷
- 已排除或降级的候选问题
- 配置风险与后续修复优先级
本文件用于替代分散的 round2 / round3 阶段记录,作为当前单一汇总版本。
---
## 一、审计范围
本轮覆盖以下服务与共享模块:
- `auth`
- `blog`
- `canvas`
- `home`
- `prompt`
- `shared`
审计方式:
- 全仓静态源码审计
- 非破坏性线上动态验证
- 仅进行低风险、只读或受控请求验证
---
## 二、总体结论
### 当前总体判断
`ephron.ren` **不是裸奔状态**,具备较明确的基础安全基线:
- 服务端 admin 鉴权存在
- 权限检查存在
- 统一安全响应头在线上已生效
- 大部分 admin 写操作具备 CSRF 防护
- service token 与 admin 边界总体存在
- blog 图片上传链具备权限、CSRF、大小限制和重编码处理
- auth 登录跳转链具备 redirect 校验
但本次审计也确认了数个真实问题,主要集中在:
- `prompt`
- `home admin`
当前最准确结论仍然是:
> **项目具备安全基线,但不能认定为“已经安全完成”;已确认若干需尽快修复的问题。**
---
## 三、已确认安全基线
## 1. 路由守卫结论
- **未发现前端 SPA 式客户端路由守卫**
- 未发现 `router.beforeEach``createRouter``react-router``middleware.ts` 等典型模式
- **存在服务端 admin 路由级鉴权/重定向**
- 各服务 admin 入口会读取 `ephron_auth` Cookie
- 校验登录态与权限
- 未登录时重定向到 `auth.ephron.ren/login`
## 2. 统一安全头在线上已确认生效
线上已确认以下响应头存在:
- `Content-Security-Policy`
- `X-Frame-Options`
- `X-Content-Type-Options: nosniff`
- `Referrer-Policy: strict-origin-when-cross-origin`
典型 CSP 现状包含:
- `frame-ancestors 'none'`
- `base-uri 'self'`
- `form-action 'self' https://*.ephron.ren`
## 3. CSRF 基线存在
`shared/csrf.py` 已实现双提交 cookie 风格 CSRF
- GET 生成 token
- 表单与 Cookie 同时携带 token
- POST 校验一致性
- token 有时效限制
- 使用安全比较
## 4. service token / admin 权限边界存在
例如 `home` admin 已明确拒绝 service token
- 返回 `403`
- 错误信息:`Service tokens cannot access home admin`
## 5. blog Markdown 正文链存在白名单净化
`blog/src/utils/markdown.py` 中:
- Markdown 渲染后经过 `bleach.clean(...)`
- 属于白名单净化,而不是裸渲染 HTML
## 6. blog 图片上传链具备基础防护
当前已确认:
- 登录权限检查
- `blog.asset.upload` 权限控制
- CSRF 校验
- 文件大小限制
- Pillow 解码后重编码为 WebP
- 写入固定上传目录
---
## 四、已确认问题
## 1. 高危:`prompt /api/test-connection` 匿名 SSRF / 外连探测面
### 结论
高危,已确认。
### 源码证据
`prompt/src/routes/api.py`
- 存在公开 `POST /api/test-connection`
- 直接接受:
- `provider`
- `base_url`
- `api_key`
- `model`
- 服务端直接对 `{payload.base_url}/chat/completions` 发请求
- 未见登录校验、权限校验、CSRF 限制
### 线上验证
已非破坏性验证:
- `POST https://prompt.ephron.ren/api/test-connection`
- 使用 `base_url=http://127.0.0.1:9`
- 返回:`200`
- 响应:`{"success":false,"error":"All connection attempts failed"}`
这表明:
- 接口匿名可访问
- 服务端确实尝试连接指定地址
- 可被用于网络探测 / SSRF 探针
### 影响
- 探测内网 / 本机 / 特定外部地址
- 回显网络结果差异
- 若未来可访问内部控制面或 metadata 地址,风险进一步升级
### 建议
1. 该接口至少改为管理员鉴权
2. `base_url` 做严格白名单
3. 禁止 localhost / 内网 / link-local / metadata 地址
4. 禁止透传详细网络错误
5. 增强审计日志与限流
---
## 2. 中危:`prompt` 调试测试链潜在 HTML 注入 / XSS
### 结论
中危,已确认存在危险链路。
### 源码证据
`prompt/static/js/test-prompt.js`
- 错误事件:
- `contentDiv.innerHTML = <div class="error">${event.detail}</div>`
- 普通异常:
- `contentDiv.innerHTML = <div class="error">${error.message}</div>`
- 流式内容:
- `contentDiv.innerHTML = this.renderMarkdown(fullContent)`
- `renderMarkdown()` 调用 `marked.parse(text)`
`prompt/src/services/llm.py` / `prompt/src/routes/api.py`
- 上游错误文本 / 响应文本可进入错误回显链
### 影响
- 管理员面自触发 / 调试型 XSS
- 若错误页或上游响应可控,可能进一步形成可利用注入
- 结合 CSP 中保留 `'unsafe-inline'`,利用门槛下降
### 建议
1. 错误展示改用 `textContent`
2. markdown 输出增加 sanitize
3. 后端不要透传原始 `response.text`
4. 仅允许可信渲染链处理富文本
---
## 3. 中危:`prompt` 登录态写接口缺 CSRF
### 结论
中危,静态确认。
### 源码证据
`prompt/src/routes/api.py` 中以下接口依赖登录态,但未见 `verify_csrf_token(...)`
- `/api/prompts/{key}/test`
- `/api/prompts/{key}/test/save-example`
其中:
- `/test` 是调试触发接口
- `/test/save-example` 是明确的状态变更接口
### 线上验证
匿名访问会被 `401` 拦截,说明依赖 cookie 登录态。当前未使用真实登录态做利用验证,因此认定为**静态确认缺陷**。
### 影响
- 登录用户可能被跨站诱导发起请求
- `/test/save-example` 可被用于跨站状态变更
### 建议
1. 给所有 cookie 登录态写接口统一补 CSRF
2. 抽成共用依赖,避免再漏
3. 补自动化测试
---
## 4. 中危:`home` admin 编辑器存在存储型 / 自触发型 DOM XSS
### 结论
中危,已确认。
### 服务端证据
`home/src/routes/admin.py`
- 把数据库内容 `json.dumps(content, ensure_ascii=False)` 注入模板变量 `content_json`
`home/src/services/content.py`
- `save_draft(content, uid)` / `publish_content(content, uid)`
- 将内容 JSON 原样入库
- 未做字段级 HTML 清洗
### 前端证据
`home/templates/admin/index.html`
- `const initialContent = JSON.parse({{ content_json | tojson }});`
- 之后多处:
- `list.innerHTML += ... value="${exp.title}" ...`
- `value="${proj.title}"`
- `textarea ...>${proj.bullets.join('\n')}</textarea>`
- `value="${skill.category}"`
- 即数据库内容进入 HTML 片段和属性值拼接
- 未见可靠转义层
### 影响
- 管理员打开编辑页即可触发 DOM XSS
- 可劫持后台操作上下文
- 可成为管理员面存储型 XSS
### 建议
1. 禁止不可信数据进入 `innerHTML +=`
2. 改为 `createElement` / `.value` / `.textContent`
3. 为含引号 / 闭合标签 / 事件属性 payload 补回归测试
---
## 五、已复核但不应误报的点
## 1. 开放重定向未确认成立
`auth` 登录跳转链调用了 `validate_redirect()`,因此本轮**未确认 auth 存在开放重定向漏洞**。
## 2. `canvas /raw/{slug}` 当前不作为确认漏洞
源码显示:
- `X-Frame-Options: SAMEORIGIN`
- raw 专用 CSP`frame-ancestors 'self'`
- 用于受限 iframe 预览
当前证据不足以确认实际越界利用。
## 3. blog 正文链当前不报直接存储型 XSS
因为存在 `bleach.clean(...)` 白名单净化。
## 4. blog 图片上传当前未见明显任意文件上传
当前证据支持其具备基础处理链保护。
---
## 六、配置风险与需持续关注项
## 1. 多服务默认 development 行为是配置陷阱
多服务存在:
- `IS_DEVELOPMENT = os.getenv("ENVIRONMENT", "development") == "development"`
- `docs_url="/docs" if IS_DEVELOPMENT else None`
- `error_message = str(exc) if IS_DEVELOPMENT else None`
这意味着:
- 若部署漏设 `ENVIRONMENT`
- 服务会默认按 development 行为运行
- 可能意外暴露 `/docs`
- 可能在 500 页面回显错误细节
### 判断
这是**高优先级配置风险**,但当前不是已确认线上漏洞。
### 建议
1. 默认值改为 production
2. 显式校验环境变量枚举值
3. 启动时拒绝未声明环境的生产部署
---
## 七、优先级排序
### P0
1. 下线或严格鉴权 `prompt /api/test-connection`
2. 修复 `home admin``innerHTML +=` 注入链
### P1
3. 修复 `prompt` 调试链 XSS
4.`prompt` 登录态写接口补 CSRF
### P2
5. 将多服务 `ENVIRONMENT` 默认值改为 production
6. 补自动化回归测试
---
## 八、最终结论
如果只问“这个项目有没有安全基线”,答案是:**有**。
如果问“这个项目现在能不能直接认定为安全”,答案是:**不能**。
本次审计完成后,更准确的说法是:
> `ephron.ren` 已具备基础安全防护,但仍存在若干已确认真实问题,尤其集中在 `prompt` 与 `home admin`,需要进入修复与回归验证阶段。