add consolidated ephron.ren security audit prd
This commit is contained in:
324
2026-05-16-ephron-security-audit-prd.md
Normal file
324
2026-05-16-ephron-security-audit-prd.md
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# ephron.ren 安全审计总 PRD(2026-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`,需要进入修复与回归验证阶段。
|
||||||
Reference in New Issue
Block a user