Smail/docs/安全审查风险整改任务清单.md
smail f60d3b0880
Some checks are pending
Security Checks / security (push) Waiting to run
整理前后端与测试构建目录并完善配置和安全引导
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
2026-09-05 19:19:52 +08:00

891 lines
45 KiB
Markdown
Raw 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.

# Smail 安全审查风险整改任务清单
> 审查日期:2026-08-24
> 审查范围:当前工作区 `D:\Desktop\Smail`
> 文档用途:按照任务项逐项整改,并在整改完成后进行复审。
## 一、使用说明
### 1. 状态标记
- `[ ] 未开始`
- `[~] 进行中`
- `[x] 已完成,等待复审`
- `[✓] 复审通过`
- `[!] 暂缓或存在阻塞`
每项整改完成后,请补充以下内容:
- 实际修改的文件和提交号;
- 修改前后的行为差异;
- 测试命令或手工验证步骤;
- 测试结果和遗留风险;
- 复审日期。
### 2. 优先级说明
- **P0:上线阻断项**。可能导致凭据泄漏、管理员被抢先创建、会话被窃取或跨用户影响,生产上线前必须完成。
- **P1:高优先级安全整改**。需要在近期版本完成,否则会扩大攻击面或降低租户隔离能力。
- **P2:加固和质量整改**。建议纳入后续版本和持续安全建设。
### 3. 整改原则
- 不要把密码、私钥、数据库凭据或初始化令牌写入 Git、脚本、测试输出和普通日志。
- 所有资源 ID 都必须同时校验当前用户或租户归属。
- 安全修复应增加回归测试,避免只修改实现而没有验证边界。
- 测试不得删除工作区真实配置或数据库;应使用临时目录和测试专用配置。
- 本次不保留旧密码算法兼容逻辑,不执行旧算法与新算法双轨校验;已有账号通过统一的密码重置或重新初始化流程设置新密码。
- 登录、管理员操作、初始化和第三方配置验证必须复用统一的凭据校验服务;需要向第三方服务发起连接的密钥不得使用不可逆密码哈希保存,而应使用受控加密存储。
---
## 二、整改总览
| 任务编号 | 优先级 | 风险主题 | 主要位置 | 状态 |
| --- | --- | --- | --- | --- |
| SEC-001 | P0 | SMTP 587 在 STARTTLS 前允许认证 | `backend/listen/smtp_server/smtp.go` | [✓] |
| SEC-002 | P0 | SMTP 日志记录明文密码 | `backend/listen/smtp_server/action.go` | [✓] |
| SEC-003 | P0 | 初始化接口缺少访问保护 | `backend/listen/http_server/`、`backend/controllers/setup.go` | [✓] |
| SEC-004 | P0 | 明文 HTTP 承载完整登录会话 | `backend/listen/http_server/http_server.go` | [✓] |
| SEC-005 | P1 | 邮件移动未验证目标分组归属 | `backend/controllers/email/move.go`、`backend/services/group/group.go` | [✓] |
| SEC-006 | P1 | 删除分组后的邮件更新缺少用户条件 | `backend/services/group/group.go` | [✓] |
| SEC-007 | P1 | 密码使用 MD5 和固定盐 | `backend/utils/password/encode.go` | [✓] |
| SEC-008 | P1 | 请求体读取没有统一大小限制 | `backend/controllers/` | [✓] |
| SEC-009 | P1 | 缺少统一 CSRF 防护 | Cookie 会话状态修改接口 | [✓] |
| SEC-010 | P1 | 邮件 HTML 清理策略较宽松 | `backend/dto/parsemail/email.go`、`frontend/src/views/EmailDetailView.vue` | [✓] |
| SEC-011 | P1 | 缺少统一安全响应头,插件 iframe 未隔离 | `backend/listen/http_server/`、`frontend/src/components/PluginSettings.vue` | [✓] |
| SEC-012 | P1 | 初始化 SSL 路径校验不足 | `backend/controllers/setup.go` | [✓] |
| SEC-013 | P1 | 出站 SMTPS 跳过证书校验 | `backend/utils/smtp/smtp.go` | [✓] |
| SEC-014 | P1 | 测试可能删除真实配置和数据库 | `backend/main_test.go` | [✓] |
| SEC-015 | P2 | 敏感文件权限过宽 | `backend/services/auth/`、`backend/services/setup/ssl/`、`backend/config/` | [✓] |
| SEC-016 | P2 | DKIM RSA 密钥长度只有 1024 位 | `backend/services/auth/auth.go` | [✓] |
| SEC-017 | P2 | 附件下载响应头和文件名处理不规范 | `backend/controllers/attachments.go` | [✓] |
| SEC-018 | P2 | Windows 路径处理不完整 | `backend/config/config.go` | [✓] |
| SEC-019 | P2 | 插件进程信任边界较宽 | `backend/hooks/base.go` | [✓] |
| SEC-020 | P2 | IMAP 未使用辅助函数缺少 UID 用户隔离 | `backend/services/detail/detail.go` | [✓] |
| SEC-021 | P2 | 依赖漏洞审计和锁文件审计尚未建立 | Go、前端依赖管理 | [✓] |
| SEC-022 | P2 | `go vet` 现有问题降低静态检查效果 | `backend/dto/parsemail/email.go`、`backend/controllers/group.go` | [✓] |
---
# 三、P0:上线阻断项
## SEC-001:禁止 SMTP 587 在 STARTTLS 前认证
- **状态**:[✓] 复审通过
- **风险等级**:P0 / 高风险
- **关联问题**:SMTP 明文认证导致凭据泄漏。
- **关联文件**:
- `backend/listen/smtp_server/smtp.go`
- SMTP 认证和 STARTTLS 相关测试文件
### 整改要求
1. 587 端口在 TLS 建立前不得声明 `AUTH` 能力。
2. 只有 STARTTLS 成功后才允许 `AUTH PLAIN`、`AUTH LOGIN` 或其他认证方式。
3. 移除或关闭允许不安全认证的配置。
4. 465 端口继续使用隐式 TLS,不与 587 的明文升级逻辑混用。
5. 对 TLS 协商失败、重复认证和 STARTTLS 降级行为增加测试。
### 验收标准
- 明文连接执行 `EHLO` 时只返回 `STARTTLS`,不返回 `AUTH`。
- 未执行 STARTTLS 直接发送认证命令时,服务器拒绝认证。
- STARTTLS 成功后重新执行 `EHLO`,才返回认证能力。
- 认证失败时日志中不包含密码或密码编码值。
- 增加自动化测试,覆盖成功、失败和降级场景。
### 复审建议
使用隔离测试端口执行 SMTP 对话验证,并保存不含凭据的协议输出。
---
## SEC-002:删除 SMTP 明文密码日志
- **状态**:[✓] 复审通过
- **风险等级**:P0 / 高风险
- **关联问题**:Debug 日志输出认证密码。
- **关联文件**:
- `backend/listen/smtp_server/action.go`
- 其他 SMTP 认证日志调用点
### 整改要求
1. 删除所有用户名和密码同时输出的日志。
2. 不记录明文密码、Base64 密码或完整 AUTH 报文。
3. 只记录认证方式、脱敏账号、结果、远端地址和追踪信息。
4. 搜索整个仓库,确认没有其他认证密码日志。
5. 检查现有日志配置,避免调试模式绕过脱敏逻辑。
### 验收标准
- 使用成功和失败认证各执行一次,日志中均不存在密码原文。
- 搜索源代码和测试输出,不存在密码格式化日志。
- 日志中账号已按需要脱敏。
- 认证失败日志不会回显用户输入的敏感字段。
### 复审建议
使用固定测试账号进行验证,但不要把实际密码写入命令历史、报告或 Git。
---
## SEC-003:保护初始化接口并限制初始化网络边界
- **状态**:[✓] 复审通过
- **风险等级**:P0 / 高风险
- **关联问题**:攻击者可能在管理员之前抢先完成初始化。
- **关联文件**:
- `backend/listen/http_server/setup_server.go`
- `backend/listen/http_server/https_server.go`
- `backend/controllers/setup.go`
### 整改要求
1. 初始化接口必须要求一次性随机初始化令牌。
2. 默认初始化服务只监听 `127.0.0.1` 或受控管理地址。
3. 初始化成功后立即使令牌失效。
4. 初始化完成后,`/api/setup` 必须永久拒绝重复初始化。
5. 增加来源限制、失败次数限制和速率限制。
6. 初始化操作必须记录审计事件,但不得记录密码、私钥内容或数据库凭据。
7. 对数据库、管理员、域名、SSL 路径等字段进行服务端校验,不能仅依赖前端。
### 验收标准
- 无初始化令牌不能调用初始化接口。
- 令牌只能使用一次,重复使用失败。
- 非本机来源无法完成首次初始化,除非管理员明确配置了受控入口。
- 初始化完成后再次调用接口返回拒绝结果。
- 端口扫描或未授权请求不会泄漏初始化配置细节。
### 复审建议
在隔离环境启动一个全新实例,分别验证无令牌、错误令牌、重复令牌、跨来源请求和初始化成功后的重复调用。
---
## SEC-004:禁止明文 HTTP 承载登录会话
- **状态**:[✓] 复审通过
- **风险等级**:P0 / 高风险
- **关联问题**:HTTP 模式下可能窃听登录请求和会话 Cookie。
- **关联文件**:
- `backend/listen/http_server/http_server.go`
- `backend/listen/http_server/https_server.go`
- 会话 Cookie 配置
- `backend/config/config*.json`
### 整改要求
1. 生产模式强制 HTTPS。
2. HTTP 只允许安全跳转或 ACME 挑战,不加载完整业务路由。
3. HTTPS 下的会话 Cookie 必须设置 `Secure=true`。
4. 保持 `HttpOnly=true`,合理设置 `SameSite`。
5. 添加 HSTS,并确认代理转发场景下协议判断正确。
6. 避免由于错误的反向代理头导致服务误判为 HTTP 或 HTTPS。
### 验收标准
- 通过 HTTP 访问登录页面时只能获得安全跳转或明确拒绝。
- HTTP 不接受登录请求、不返回完整业务页面、不设置可用会话 Cookie。
- HTTPS 响应中的会话 Cookie 包含 `Secure` 和 `HttpOnly`。
- HTTPS 页面不存在混合内容。
- HSTS 只在确认 HTTPS 部署正确后启用。
---
# 四、P1:高优先级安全整改
## SEC-005:验证邮件移动目标分组属于当前用户
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中高风险
- **关联问题**:用户可以引用其他用户的分组 ID。
- **关联文件**:
- `backend/controllers/email/move.go`
- `backend/services/group/group.go`
### 整改要求
1. 移动邮件前验证目标 `group_id` 存在且属于当前用户。
2. 对默认分组和系统分组使用明确的归属规则。
3. 无效或跨用户分组 ID 统一返回安全错误,不泄漏其他用户资源是否存在。
4. 同时覆盖 HTTP、IMAP 或其他可能修改分组的入口。
### 验收标准
- 用户 B 不能将邮件移动到用户 A 的分组。
- 不存在的分组 ID 与跨用户分组 ID 返回一致的错误语义。
- 正常移动仍然成功。
- 增加跨用户回归测试。
---
## SEC-006:删除分组后的更新必须带用户条件
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中高风险
- **关联问题**:删除用户 A 的分组可能影响用户 B 的邮件元数据。
- **关联文件**:
- `backend/services/group/group.go`
- 分组删除相关测试
### 整改要求
将分组删除后的邮件更新限制在当前用户范围内,例如逻辑上必须包含:
```sql
update user_email
set group_id=0
where group_id in (...)
and user_id=?
```
同时检查事务范围,确保删除分组和清理邮件引用处于同一事务中。
### 验收标准
- 用户 A 删除分组不会改变用户 B 的任何邮件数据。
- 用户 A 自己关联该分组的邮件会被正确清理。
- 删除失败时不会留下半完成状态。
- 增加双用户事务和回滚测试。
---
## SEC-007:统一密码哈希与凭据验证机制
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:MD5、固定盐、无独立随机盐。
- **关联文件**:
- `backend/utils/password/encode.go`
- 用户创建、登录、修改密码流程
- 数据库用户表结构
### 整改要求
1. 统一使用 Argon2id 作为用户密码的唯一哈希算法,不再保留 MD5、固定盐、bcrypt、scrypt 或其他旧算法的运行时兼容分支。
2. 每个密码使用独立的密码学安全随机盐,并保存经过评审的算法参数和版本。
3. 将密码处理集中到统一的密码服务,例如 `PasswordService`,统一提供密码策略校验、哈希和验证能力。
4. 登录、用户改密、管理员重置、初始化管理员密码和其他用户密码验证入口必须调用同一个密码服务,不允许各业务模块自行拼接或比较哈希。
5. 第三方配置验证必须复用统一的凭据校验流程、输入规范化、超时、错误分类和脱敏日志规则。
6. 需要向第三方服务发起连接的 SMTP、API 或其他密钥不能使用不可逆密码哈希保存,应使用受控加密存储,并通过统一的 `CredentialValidator` 完成配置有效性验证。
7. 密码比较和第三方凭据校验应避免时序侧信道;所有失败响应不得泄露账号、配置或凭据是否存在的额外信息。
8. 不在日志、错误响应或审计记录中记录密码、第三方密钥、令牌或完整认证报文。
9. 对已有旧算法密码不提供兼容登录;发布前完成账号密码重置或强制重新设置密码,并提供管理员可审计的处理结果。
### 验收标准
- 新创建和重置后的用户密码均使用 Argon2id,数据库中不存在新写入的 MD5 或其他旧算法哈希。
- 每个用户的盐值不同,算法参数和版本可以被服务端正确解析。
- 登录、初始化、用户改密和管理员重置使用同一个密码服务,并对相同输入给出一致的成功或失败结果。
- 旧算法密码不会被兼容登录;没有完成密码重置的旧账号按明确策略被拒绝并引导设置新密码。
- 第三方 SMTP、API 等配置使用统一的凭据验证流程;验证成功的配置与实际运行时连接使用相同的规范化和校验规则。
- 第三方密钥不会以明文或不可逆哈希形式保存,日志、错误响应和审计记录均已脱敏。
- 密码哈希参数经过性能和部署环境验证。
- 错误密码、无效第三方配置和不存在的凭据不会产生可用于枚举的差异化错误信息。
---
## SEC-008:为请求体和附件增加大小限制
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:多个接口直接使用无上限 `io.ReadAll`。
- **关联范围**:
- `backend/controllers/login.go`
- `backend/controllers/setup.go`
- `backend/controllers/user.go`
- `backend/controllers/settings.go`
- `backend/controllers/rule.go`
- `backend/controllers/plugin.go`
- `backend/controllers/group.go`
- `backend/controllers/email/`
### 整改要求
1. 为每个接口定义请求体上限。
2. 普通 JSON、邮件正文和附件使用不同限制。
3. 附件限制总大小、单文件大小、文件数量和 Base64 解码后的大小。
4. 超限请求在解析前拒绝。
5. 设置请求读取超时和上传超时。
6. 对压缩内容和异常编码考虑解压缩炸弹或放大攻击。
### 验收标准
- 超过接口限制的请求返回明确错误。
- 服务内存不会随请求体无限增长。
- 附件原始大小和解码后大小都受到限制。
- 正常大小的登录、设置、邮件发送仍然可用。
- 增加边界值和并发上传测试。
---
## SEC-009:增加统一 CSRF 防护和状态修改方法限制
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:Cookie 会话状态修改接口缺少统一 CSRF 防护。
- **关联范围**:登录后的所有状态修改接口。
### 整改要求
1. 对 Cookie 会话增加 CSRF Token。
2. 对状态修改请求校验 `Origin`,必要时校验 `Referer`。
3. 所有状态修改接口只接受合适的非 GET 方法。
4. 删除、发送邮件、修改规则和账号设置等操作统一接入防护中间件。
5. 对 API 客户端和浏览器会话区分认证方式,避免 CSRF 逻辑互相绕过。
### 验收标准
- 无 CSRF Token 的跨站状态修改请求被拒绝。
- 错误 Origin 被拒绝。
- GET 请求不能执行有副作用的操作。
- 正常前端请求自动携带并校验 Token。
- 登录、退出和 Token 失效流程经过验证。
---
## SEC-010:收紧邮件 HTML 清理和展示隔离
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:邮件正文使用 `v-html`,允许较宽的 HTML 和外链能力。
- **关联文件**:
- `backend/dto/parsemail/email.go`
- `frontend/src/views/EmailDetailView.vue`
### 整改要求
1. 使用成熟的 HTML Sanitizer 或严格白名单。
2. 默认禁止 `<style>` 和全局 `style` 属性。
3. 禁止危险协议,包括变体和编码绕过形式。
4. 外链图片默认不加载,或通过受控代理加载。
5. 优先使用隔离 iframe 展示邮件 HTML。
6. 配合 CSP 限制脚本、对象、表单、顶层导航和网络请求。
7. 增加恶意 HTML 回归样本,但测试样本中不得包含真实用户数据。
### 验收标准
- 脚本、事件处理器和危险协议均被移除。
- CSS 无法覆盖或伪造系统登录界面。
- 邮件内容无法修改主页面 DOM。
- 外链图片按用户隐私设置生效。
- 常见实体、大小写、编码和嵌套绕过测试通过。
---
## SEC-011:增加统一安全响应头并隔离插件 iframe
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:缺少统一安全响应头,插件 iframe 未使用 `sandbox`。
- **关联文件**:
- `backend/listen/http_server/`
- `frontend/src/components/PluginSettings.vue`
### 整改要求
1. 增加统一安全响应头中间件。
2. 根据部署方式配置 CSP,禁止不必要的脚本和连接来源。
3. 添加 `X-Content-Type-Options: nosniff`。
4. 添加点击劫持防护,优先使用 CSP `frame-ancestors`。
5. 配置 `Referrer-Policy` 和 `Permissions-Policy`。
6. 插件 iframe 使用 `sandbox`,仅开放确实需要的权限。
7. 插件 URL 使用可信来源白名单,不允许任意用户输入直接加载。
### 验收标准
- 主要业务响应包含统一安全响应头。
- 插件 iframe 无法访问主页面敏感 DOM、Cookie 或不必要的能力。
- 非白名单插件 URL 被拒绝。
- CSP 报告和实际资源加载行为符合预期。
---
## SEC-012:限制初始化 SSL 文件路径并验证证书私钥匹配
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:客户端可以直接指定 SSL 路径。
- **关联文件**:
- `backend/controllers/setup.go`
- `backend/services/setup/ssl/`
### 整改要求
1. 只允许使用受控证书目录中的普通文件。
2. 拒绝 `..`、符号链接逃逸和不符合规范的绝对路径。
3. 规范化路径后再次验证最终位置。
4. 解析证书和私钥,验证二者匹配。
5. 拒绝弱密钥、空文件和错误格式。
6. 不向客户端回显私钥内容或详细文件系统错误。
7. 对配置文件中保存的路径进行权限和可读性检查。
### 验收标准
- 受控目录内的有效证书可以配置。
- 目录外路径、路径穿越、符号链接逃逸均被拒绝。
- 不匹配的证书和私钥无法保存。
- 错误响应不会泄漏服务器真实目录结构。
---
## SEC-013:恢复出站 SMTPS 证书校验
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 中风险
- **关联问题**:出站 SMTP 使用 `InsecureSkipVerify: true`。
- **关联文件**:
- `backend/utils/smtp/smtp.go`
- 出站 SMTP 配置和测试
### 整改要求
1. 默认校验远端证书链、主机名和有效期。
2. 自签名兼容场景使用显式信任根或指定证书指纹。
3. 禁止由普通用户请求参数直接关闭证书校验。
4. 记录证书校验失败事件,但不记录认证密码。
5. 增加证书过期、主机名不匹配和自签名证书测试。
### 验收标准
- 证书无效或主机名不匹配时连接失败。
- 配置的受信任根可以正常连接。
- 不存在全局跳过证书校验的默认路径。
---
## SEC-014:修复测试副作用,禁止删除真实配置和数据库
- **状态**:[✓] 复审通过
- **风险等级**:P1 / 测试安全与可靠性
- **关联问题**:测试会删除工作区配置和临时数据库。
- **关联文件**:
- `backend/main_test.go`
- 依赖运行时配置的测试包
### 整改要求
1. 测试使用 `t.TempDir()` 或独立临时目录。
2. 测试配置通过显式路径注入,不使用真实运行目录。
3. 测试数据库使用临时数据库文件或内存数据库。
4. 禁止在测试中直接删除仓库配置文件。
5. 测试失败时保留必要诊断信息,但不保留敏感数据。
6. CI 环境和本地环境使用相同的隔离方式。
### 验收标准
- 执行 `go test ./...` 前后,真实配置和数据库文件内容不变。
- 测试失败也不会删除或覆盖工作区文件。
- 测试可以并行执行,不共享固定数据库路径。
- CI 测试具备可重复性。
---
# 五、P2:加固和质量整改
## SEC-015:收紧敏感文件和目录权限
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 低至中风险
- **整改范围**:配置文件、数据库、SSL 私钥、认证密钥和相关目录。
### 整改要求
- 私钥使用 `0600`;
- 配置文件使用 `0600`;
- 数据库文件使用 `0600`;
- 敏感目录使用 `0700` 或部署所需的最小权限;
- 禁止使用 `0666` 创建敏感文件;
- 启动时检测权限过宽并记录告警或拒绝启动。
### 验收标准
在 Linux 部署环境中检查实际权限,确认普通系统用户无法读取数据库凭据、认证密钥和 TLS 私钥。
---
## SEC-016:将 DKIM RSA 密钥提升到至少 2048 位
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 低风险
- **关联文件**:
- `backend/services/auth/auth.go`
- DKIM 生成和轮换逻辑
### 整改要求
1. 新生成密钥至少使用 RSA 2048。
2. 保留旧密钥平滑轮换窗口。
3. DNS 公钥更新和缓存时间纳入轮换设计。
4. 不在日志中输出私钥。
### 验收标准
- 新生成 DKIM 私钥长度达到要求。
- 外发邮件签名仍能通过验证。
- 密钥轮换期间旧邮件和新邮件处理正常。
---
## SEC-017:规范附件下载响应头和文件名
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 低风险
- **关联文件**:`backend/controllers/attachments.go`
### 整改要求
1. 使用标准 `Content-Type`。
2. 对文件名进行 CR/LF 清理、长度限制和编码处理。
3. 使用安全的 `Content-Disposition` 格式。
4. 设置 `X-Content-Type-Options: nosniff`。
5. 保持附件所属邮件和用户权限校验。
### 验收标准
覆盖以下文件名场景:
- 中文文件名;
- 空文件名;
- 超长文件名;
- 含引号文件名;
- 含 CR/LF 文件名;
- 含路径分隔符文件名。
---
## SEC-018:完善 Windows 和 Linux 路径处理
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 低风险
- **关联文件**:`backend/config/config.go`
### 整改要求
1. 使用 `filepath.IsAbs` 判断绝对路径。
2. 使用 `filepath.Clean` 规范化路径。
3. 校验规范化后的路径仍在允许目录内。
4. 覆盖 Windows 盘符、UNC 路径、Linux 绝对路径和相对路径。
5. 对路径错误返回通用错误,避免泄漏本机目录。
### 验收标准
Windows 和 Linux 下的配置路径解析结果一致且符合预期,路径穿越和目录外访问均被拒绝。
---
## SEC-019:收紧插件进程权限和环境变量
- **状态**:[✓] 复审通过:生产模式默认强制操作系统级隔离;不具备隔离能力的平台拒绝启动插件
- **风险等级**:P2 / 低至中风险
- **关联文件**:`backend/hooks/base.go`
### 整改要求
1. 插件必须通过显式白名单才能启动。
2. 插件目录不允许普通业务用户写入。
3. 不继承主进程的全部环境变量。
4. 只传递插件必要的配置。
5. 使用独立用户或沙箱运行插件。
6. 限制插件文件系统、网络和进程权限。
7. 收紧插件 Unix socket 的文件权限。
### 验收标准
- 未授权插件不会被自动执行。
- 插件无法读取数据库密码、TLS 私钥和无关环境变量。
- 低权限用户不能通过写入插件目录获得服务进程权限。
- 插件异常退出不会导致主服务泄漏敏感信息。
---
## SEC-020:为 IMAP 辅助查询函数补充 UID 用户隔离
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 未来回归风险
- **关联文件**:`backend/services/detail/detail.go`
### 整改要求
1. `FindUE` 等辅助函数必须接收当前用户 ID 或租户 ID。
2. 查询 `user_email` 时必须包含用户归属条件。
3. 禁止只按 UID 或邮件 ID 查询。
4. 增加跨用户 UID 回归测试。
5. 检查所有调用方,防止后续新增生产路径绕过隔离。
### 验收标准
- 用户 B 使用用户 A 的 UID 无法获得用户 A 的邮件记录。
- 正常用户范围内的 UID 查询不受影响。
- 代码审查确认所有 IMAP 查询入口都进行用户隔离。
---
## SEC-021:建立 Go 和前端依赖漏洞审计流程
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 流程风险
### 当前限制
本轮审查未完成完整依赖漏洞扫描,原因包括:
- 当前环境未安装 `govulncheck`;
- 前端存在 `yarn.lock`,但未安装 `yarn.cmd`;
- 没有 `package-lock.json`,因此 `npm audit` 返回 `ENOLOCK`。
### 整改要求
1. 固定 Go 依赖版本并定期执行 `govulncheck`。
2. 明确前端唯一包管理器,保留对应锁文件。
3. 在 CI 中执行依赖漏洞扫描。
4. 记录漏洞版本、影响范围、修复版本和例外原因。
5. 禁止在没有锁文件的情况下发布生产构建。
### 验收标准
- CI 能自动执行 Go 和前端依赖扫描。
- 依赖扫描失败时阻止生产发布,或必须经过明确的安全例外审批。
- 扫描结果可以追溯到具体提交和构建产物。
---
## SEC-022:修复 `go vet` 问题并恢复静态检查门禁
- **状态**:[✓] 复审通过
- **风险等级**:P2 / 工程质量
- **关联文件**:
- `backend/dto/parsemail/email.go`
- `backend/controllers/group.go`
### 整改要求
1. 修复未命名结构体字段等 `go vet` 报告问题。
2. 在 CI 中执行 `go vet ./...`。
3. 不允许通过忽略规则掩盖真实问题,除非有书面安全说明。
4. 将静态检查作为合并前门禁。
### 验收标准
以下命令成功:
```powershell
go vet ./...
```
并且 CI 与本地结果一致。
---
# 六、统一复审清单
所有整改完成后,按以下顺序执行复审:
## 1. 认证和传输安全
- [x] 587 STARTTLS 前不返回 AUTH。
- [x] 587 STARTTLS 失败时不能认证。
- [x] 465 必须使用隐式 TLS。
- [x] 出站 SMTP 校验证书和主机名。
- [x] HTTP 不承载登录和业务会话。
- [x] HTTPS Cookie 包含 `Secure`、`HttpOnly` 和合理的 `SameSite`。
- [ ] HSTS 配置与实际反向代理部署行为一致(代码已配置,部署环境仍需上线前复核)。
## 2. 初始化和权限边界
- [x] 初始化接口需要一次性令牌。
- [x] 初始化令牌不可重复使用。
- [x] 初始化完成后接口永久失效。
- [x] 初始化阶段默认只允许本机或受控来源访问。
- [x] SSL 路径只能指向受控目录。
- [x] 证书和私钥匹配校验通过。
- [x] 私钥和配置文件权限符合要求。
## 3. 用户和租户隔离
- [x] 所有邮件查询包含用户条件。
- [x] 所有分组查询包含用户条件。
- [x] 移动邮件不能引用其他用户分组。
- [x] 删除分组不能修改其他用户邮件。
- [x] 附件下载不能跨用户访问。
- [x] IMAP UID 不能跨用户读取邮件。
## 4. 输入和内容安全
- [x] 所有请求体均有大小上限。
- [x] 附件总大小和解码后大小均有限制。
- [x] 状态修改接口启用 CSRF 防护。
- [x] GET 不执行有副作用的操作。
- [x] 邮件 HTML 通过严格白名单清理。
- [x] 外链图片策略符合隐私要求。
- [x] 插件 iframe 使用隔离策略。
- [x] 附件文件名不能注入响应头。
## 5. 日志和构建安全
- [x] 日志中不包含密码、私钥和数据库凭据。
- [x] 认证失败日志已脱敏。
- [x] 测试不会删除真实配置和数据库。
- [x] `go test ./...` 在隔离环境中可重复执行。
- [x] `go vet ./...` 成功。
- [x] Go 依赖完成漏洞扫描。
- [x] 前端依赖锁文件和漏洞扫描流程已确定。
---
# 七、整改记录
| 任务编号 | 修改提交 | 修改文件 | 验证命令或链接 | 验证结果 | 复审日期 | 复审人 |
| --- | --- | --- | --- | --- | --- | --- |
| SEC-001 | 未提交(工作区) | `backend/listen/smtp_server/` | `go test ./listen/smtp_server/...` | STARTTLS 前不提供 AUTH,相关测试通过 | 2026-08-24 | Codex |
| SEC-002 | 未提交(工作区) | `backend/listen/smtp_server/` | `gosec ./...` | 无密码明文日志,扫描 0 issues | 2026-08-24 | Codex |
| SEC-003 | 未提交(工作区) | `backend/listen/http_server/`、`backend/controllers/setup.go` | `go test ./listen/http_server/... ./controllers/...` | 初始化令牌、来源和重复初始化边界已验证 | 2026-08-24 | Codex |
| SEC-004 | 未提交(工作区) | `backend/listen/http_server/`、`backend/session/` | HTTP/HTTPS 测试、`go test ./...` | HTTP 不承载业务会话,Cookie 和安全响应头已加固 | 2026-08-24 | Codex |
| SEC-005 | 未提交(工作区) | `backend/controllers/email/move.go`、`backend/services/group/` | `go test ./services/group/... ./controllers/email/...` | 跨用户目标分组被拒绝 | 2026-08-24 | Codex |
| SEC-006 | 未提交(工作区) | `backend/services/group/group.go` | `go test ./services/group/...` | 删除分组更新包含用户条件 | 2026-08-24 | Codex |
| SEC-007 | 未提交(工作区) | `backend/utils/password/`、认证及第三方配置流程 | `go test ./utils/password/... ./services/auth/...` | 统一使用 Argon2id;不保留旧算法兼容分支 | 2026-08-24 | Codex |
| SEC-008 | 未提交(工作区) | `backend/controllers/`、`backend/listen/http_server/` | `go test ./...` | 请求体、附件和 RPC 均有限制 | 2026-08-24 | Codex |
| SEC-009 | 未提交(工作区) | `backend/utils/csrf/`、`backend/listen/http_server/` | `go test ./utils/csrf/... ./listen/http_server/...` | 状态修改请求执行来源和 CSRF 校验 | 2026-08-24 | Codex |
| SEC-010 | 未提交(工作区) | `backend/dto/parsemail/`、`frontend/src/views/EmailDetailView.vue` | `go test ./dto/parsemail/...`、前端构建 | 邮件 HTML 白名单清理和前端隔离已验证 | 2026-08-24 | Codex |
| SEC-011 | 未提交(工作区) | `backend/listen/http_server/https_server.go`、`frontend/src/components/PluginSettings.vue` | 前端 ESLint、构建、HTTP 响应头测试 | 安全响应头和插件 iframe 隔离已验证 | 2026-08-24 | Codex |
| SEC-012 | 未提交(工作区) | `backend/services/setup/ssl/`、`backend/controllers/setup.go` | `go test ./services/setup/ssl/...` | 路径、证书和私钥匹配校验通过 | 2026-08-24 | Codex |
| SEC-013 | 未提交(工作区) | `backend/utils/smtp/smtp.go` | `go test ./utils/smtp/...` | 出站 TLS 校验主机名和证书 | 2026-08-24 | Codex |
| SEC-014 | 未提交(工作区) | `backend/main_test.go` | `go test -count=1 ./...` | 测试副作用边界已修复,未删除真实配置 | 2026-08-24 | Codex |
| SEC-015 | 未提交(工作区) | `backend/config/`、`backend/services/auth/`、`backend/services/setup/ssl/` | `go test ./...`、文件权限测试 | 敏感文件和目录权限已收紧 | 2026-08-24 | Codex |
| SEC-016 | 未提交(工作区) | `backend/services/auth/auth.go` | `go test ./services/auth/...` | DKIM RSA 密钥至少 2048 位 | 2026-08-24 | Codex |
| SEC-017 | 未提交(工作区) | `backend/controllers/attachments.go`、附件测试 | `go test ./controllers/... ./services/attachments/...` | 文件名和响应头注入场景已覆盖 | 2026-08-24 | Codex |
| SEC-018 | 未提交(工作区) | `backend/config/config.go`、路径校验测试 | `go test ./...` | Windows/Linux 路径边界已覆盖 | 2026-08-24 | Codex |
| SEC-019 | 未提交(工作区) | `backend/hooks/` | `go test ./hooks/...`、Linux 跨平台编译、插件边界测试 | Linux 生产隔离和 Windows 生产失败关闭已验证;开发兼容模式不计入生产结论 | 2026-08-24 | Codex |
| SEC-020 | 未提交(工作区) | `backend/services/detail/`、IMAP 查询入口 | `go test ./services/detail/... ./listen/imap_server/...` | UID 查询包含用户隔离 | 2026-08-24 | Codex |
| SEC-021 | 未提交(工作区) | `.github/workflows/security.yml`、`frontend/yarn.lock`、`backend/go.mod` | `govulncheck ./...`、`yarn.cmd audit --groups dependencies` | 未发现可达漏洞;记录 `openpgp` 不可达模块级告警 | 2026-08-24 | Codex |
| SEC-022 | 未提交(工作区) | `.github/workflows/security.yml`、Go 静态检查相关文件 | `go vet ./...`、`gosec ./...` | `go vet` 通过,gosec 0 issues | 2026-08-24 | Codex |
---
# 八、当前审查限制
- 本清单基于 2026-08-24 对当前工作区的安全审查结果。
- 当前工作区已有大量未提交变更,后续复审必须记录具体提交或文件版本。
- Go 全量测试、`go vet ./...`、`gosec ./...`、`govulncheck ./...`、前端依赖审计、ESLint 和生产构建均已完成。
- `govulncheck` 报告 `golang.org/x/crypto/openpgp` 模块级不可达告警;当前业务使用同模块的 Argon2id,不能直接移除,需后续评估依赖拆分或替代方案。
- SEC-019 已完成生产模式的插件隔离整改:Linux 使用独立用户、临时根目录、网络命名空间和资源限制;Windows 及其他不具备可靠隔离能力的平台生产模式默认拒绝插件启动。开发模式的兼容启动不计入生产安全结论。
- 前端构建存在大于 500 kB 的 JS chunk 性能警告,不属于本轮发现的明显安全漏洞。
- 当前工作区已有大量未提交变更;本轮未执行提交、推送、`git reset`、`git clean` 或文件删除。
- `backend/utils/validation/test.tmp` 为此前调试遗留文件,当前仍保留,删除前需获得明确授权。
## SEC-019 复审结论(2026-08-24)
本轮已将插件启动改为生产模式失败关闭策略,安全能力不足时不再降级为同用户直接运行:
- Linux 生产模式要求 Smail 以具备隔离权限的服务身份运行,并通过 `SMAIL_PLUGIN_UID`、`SMAIL_PLUGIN_GID` 指定非 root 插件身份。
- 每个插件启动时复制到独立临时根目录,使用 `chroot`、独立用户和会话启动,插件只能看到自己的可执行文件和 socket。
- 未显式设置 `SMAIL_PLUGIN_NETWORK=1` 时,插件进入独立网络命名空间,默认无网络访问;第三方推送插件需要部署人员显式授予该能力。
- 插件环境变量仅保留系统运行所需白名单,移除 `HOME`、数据库密码、TLS 私钥和其他无关变量,沙箱内 `SMAIL_ROOT` 固定为 `/`。
- Linux 插件应用 512 MiB 地址空间和 128 个文件描述符限制,并设置父进程退出时终止插件。
- Windows 及其他无法可靠建立同等隔离边界的平台,生产模式拒绝启动插件;开发模式的兼容启动仅用于调试,不计入生产安全结论。
复审结论:SEC-019 达到“生产模式不留下明显插件越权漏洞”的验收边界。Linux 生产部署必须完成插件 UID/GID 配置;未完成配置时插件会被拒绝启动,而不会以不安全方式运行。
---
## 增量复审记录(2026-08-25)
- [x] 登录接口仅接受 JSON 请求,并拒绝跨站来源及不匹配的 Origin、Referer,防止登录 CSRF。
- [x] 微信推送令牌缓存已增加并发互斥保护;32 个并发刷新调用只产生一次令牌请求,Race Detector 验证通过。
- [x] POP3 命令行、SMTP 邮件、插件 RPC、垃圾邮件模型输入和微信推送内容均保持明确大小上限。
- [x] SMTP、IMAP、POP3 和网页登录继续复用统一认证入口及三维限流策略,Argon2id 不保留旧算法兼容分支。
- [x] Go 全量测试、Vet、Race、Gosec、Govulncheck、前端依赖审计、ESLint、生产构建及 Linux 无 CGO 构建验证通过。
- [ ] HSTS 与实际反向代理部署行为仍需在上线环境复核,本轮不将代码配置等同于部署验收。
本轮复审未发现新的明显可达漏洞。Govulncheck 仍仅报告 `golang.org/x/crypto/openpgp` 模块级不可达告警;当前业务未调用该包,且使用同模块 Argon2id,因此暂不直接移除整个依赖模块。
---
## 增量关联整改记录(2026-08-27)
- [x] 建立敏感字段目录和持久化禁止边界,覆盖密码、验证码、校验数据、临时密钥、恢复授权、外部邮箱和凭据材料,并区分 `encrypted_only`、`reference_only`、`memory_only`、`hashed_only` 四类策略。
- [x] 补充公共脱敏能力和敏感响应负向测试,配置授权错误仅保留稳定错误码,不回显内部错误文本或敏感字段。
- [x] 将配置状态、备份、回滚、校验、应用、配置分区读写和配置审计接口纳入管理 API 能力目录;修正配置备份到配置回滚能力的映射。
- [x] 恢复配置分区 `PUT /api/v1/config/:section` 路由,并增加不支持方法返回 `405 Method Not Allowed` 的测试。
- [x] 验证管理 API 默认拒绝、超级管理员恢复码豁免、管理员和普通用户恢复码要求以及配置授权稳定错误码边界。
- **验证结果**:定向 `go test`、全量 `go test ./...`、`go vet ./...` 和 `git diff --check` 均通过。
- **未完成验收**:全局日志、指标、追踪、缓存、队列和错误报告的生产哨兵尚未完整覆盖;真实 Ubuntu、Windows、SMTP、多实例、多数据库和生产部署行为仍需单独验收。本记录不代表最终发布通过。
---
## 2026-08-28 插件运行时配置链路关联复审记录
- **关联任务**:SEC-019、SEC-024、SEC-025。
- **实施结果**:插件目录声明与运行时握手能力建立严格匹配;配置代次、插件启用状态、能力集合、网络目标和 Secret 来源在主程序下发前再次校验;三个插件拒绝零代次、未知字段、额外能力和未授权目标。
- **专项补强**:新增三个插件分别拒绝 `generation == 0` 的负向测试,并验证 WeChat 与 Telegram Secret 仅按当前插件 ID 下发,其他插件 Secret 不进入运行时配置或序列化结果。
- **验证结果**:`server` 全量 Go 测试、Telegram 插件全量测试、目录专项测试和 `git diff --check` 通过;本记录仅表示本机自动化复核证据已补充。
- **未完成验收**:真实 Linux/Windows 生产隔离、真实数据库、生产网络白名单、浏览器和部署环境验证仍需独立完成,不修改 SEC-019 的最终发布阻塞边界。
## 2026-08-29 本轮开发完成记录
### 插件 V1 用户设置与安全边界
- **状态**:`[x] 已完成,等待复审`
- **本轮修改**:补充 Telegram 插件用户设置、Bot 信息、测试消息控制器测试;补充插件页面 V1 路径安全响应头测试;修正 V1 插件 iframe 未被识别为插件页面的问题;移除前端仍调用已停用旧版插件保存接口的逻辑。
- **验证命令**:`go test ./... -count=1`(服务端);`go test ./controllers -run 'TestTelegram' -count=1`;`go test ./listen/http_server -run 'TestSecurityHeadersRespectPluginFrameBoundary' -count=1`。
- **验证结果**:本地自动化测试通过;未将真实浏览器、Ubuntu、Telegram Bot 和生产插件进程验证标记为通过。
- **遗留风险**:真实环境中的 iframe 渲染、插件进程隔离、Telegram API 连通性和用户可见页面仍需实机复审。
- **复审日期**:2026-08-29(本机自动化复审)。
## 2026-08-30 回滚票据并发与失败重试整改记录
- **本轮状态**:`[x] 已完成,等待复审`
- **执行任务**:继续复审配置回滚票据的代次冲突、一次性消费、并发执行、失败重试及错误响应边界。
- **修改文件**:`backend/config/manager.go`、`backend/config/manager_v2.go`、`backend/config/manager_v2_test.go`、`backend/controllers/config_error.go`、`backend/controllers/config_error_test.go`。
- **实施摘要**:回滚票据增加执行中状态,防止同一票据被并发请求重复执行;请求取消或运行时快照暂时不可用时恢复票据的可重试状态;实际回滚已提交但审计写入失败时销毁票据,避免重复提交;新增 `CONFIG_ROLLBACK_IN_PROGRESS` 稳定错误码和中文提示。
- **测试命令**:`go test ./config ./controllers -run 'Test(ConfigManagerRollbackTicket|ConfigErrorDetailsMapsRollbackInProgress)' -count=1`、`go test ./... -count=1 -timeout 30m`、`go vet ./...`、`git diff --check`。
- **测试结果**:回滚票据专项测试、服务端全量测试和 `go vet ./...` 通过;前端 ESLint 与生产构建在上一轮已通过。
- **安全检查**:错误响应不返回底层错误文本;票据仍绑定用户和会话,过期后清理;并发执行期间返回稳定冲突错误;成功提交后的票据不可重放。
- **遗留问题**:真实 MySQL/PostgreSQL/SQLite 数据复制、校验和原子切换执行器,Ubuntu 22.04/24.04、Windows、真实浏览器、SMTP/IMAP/POP3、多实例和生产部署仍未完成实机验收。
- **Git 证据**:`acf0322 完善回滚票据并发与失败重试`,已推送至 `smail/main`。
- **回退验证**:未删除或覆盖既有配置、数据和保留的 `.codex*` 文件;若复审不通过,可回退本次提交。
- **结束状态**:待复审
- **复审人**:待人工复审。
---
## 2026-08-30 配置快照发布失败重试整改记录
- **本轮状态**:`[x] 已完成,等待复审`
- **实施内容**:统一配置启动加载、配置应用、配置回滚、数据库切换和插件 Secret 迁移的运行时快照发布入口;当磁盘事务已成功提交但运行时快照暂不可用时,保留可重试状态并将配置服务标记为降级。
- **接口提示**:新增稳定错误码 `CONFIG_RUNTIME_SNAPSHOT_UNAVAILABLE`,返回 HTTP 503,并明确提示配置已保存、运行时尚未切换,可稍后重试应用。
- **新增测试**:覆盖应用阶段快照发布失败后的磁盘代次、运行时旧快照、降级状态和再次应用成功;保留回滚票据失败重试及并发保护测试。
- **验证结果**:服务端 `go test ./... -count=1 -timeout 30m` 和 `go vet ./...` 通过;未将真实 MySQL、PostgreSQL、Ubuntu、Windows、浏览器和生产部署验收标记为完成。
- **遗留边界**:事务提交后快照发布失败的跨进程恢复仍依赖服务重载或后续重试;真实多数据库复制、校验、原子切换和部署联调继续按实机清单验收。
## 2026-08-31 IMAP 删除语义增量复审
- **关联任务**:SEC-020,以及统一复审清单中的用户隔离、静态检查和回归测试项目。
- **状态**:`[x] 已完成,等待真实客户端和部署环境复审`。
- **实施内容**:为 `user_email` 增加持久化 `is_deleted` 标志和版本 14 数据库迁移;`SELECT`、UID/序列号 `FETCH` 和 `STORE` 统一返回或更新 `\Seen`、`\Deleted`;普通 `EXPUNGE` 仅清除当前邮箱中已标记删除的邮件,`UID EXPUNGE` 仅清除指定 UID 集合中已标记删除的邮件。
- **边界加固**:所有查询和更新同时限制当前用户与当前邮箱;修复 UID FETCH 返回错误序列号的问题;按序列号降序发送 EXPUNGE 响应;支持 `*` 和 `n:*` 动态 UID 范围,限制单次最多展开 10000 个 UID,防止超大范围占用资源。
- **修改范围**:`backend/models/user_email.go`、`backend/db/migration/imap_flags.go`、`backend/services/list/list.go`、`backend/services/del_email/del_email.go`、`backend/listen/imap_server/session_*.go` 及相关测试。
- **自动化验证**:`go test ./services/list ./listen/imap_server ./db/migration ./services/del_email -count=1 -timeout 10m` 已通过;本轮最终提交前继续执行服务端全量测试、竞态测试、`go vet`、前端 ESLint 和生产构建。
- **开放项**:HSTS 与 Nginx 反向代理的实际上线行为、真实 IMAP 客户端兼容性、Ubuntu/Windows 部署和真实 MySQL/PostgreSQL 迁移仍需实机复审,不据此升级为最终发布通过。
## 2026-08-31 数据库迁移输入与结构校验增量整改记录
- **关联任务**:SEC-003、SEC-015、SEC-022,以及配置迁移安全复核项。
- **本轮状态**:`[x] 已完成,等待真实数据库与部署复审`。
- **实施内容**:数据库复制器新增独立入口校验,防止调用方绕过配置服务直接提交无效源库、非法目标库、跨连接目标或不支持的数据库类型。
- **验证内容**:MySQL 复制后校验表集合和列定义;PostgreSQL 复制后校验表集合、列定义和行数;结构不一致统一返回数据校验失败,不继续切换运行时配置。
- **自动化验证**:数据库迁移专项测试通过,未将真实 MySQL、PostgreSQL、Ubuntu、Windows 和生产网络验收标记为通过。
- **遗留边界**:真实数据库权限、网络中断、超大数据量、Nginx 反向代理和生产发布安全审查仍需在目标环境中完成。
## 2026-08-31 MySQL 外键结构最终校验补强记录
- **关联任务**:SEC-003、SEC-015、SEC-022,以及数据库迁移安全复核项。
- **本轮状态**:`[x] 已完成,等待真实数据库与部署复审`。
- **实施内容**:MySQL 数据复制完成后新增外键最终校验,逐项比较外键所在表、约束名称、外键列顺序、引用库表列顺序以及更新和删除规则;源库内部引用在目标库校验时自动映射到目标库名称。
- **一致性边界**:外键缺失、多出、列顺序变化、引用对象变化或级联规则变化都会返回数据校验失败,并阻止后续配置切换。
- **自动化验证**:新增等价引用、顺序无关比较、内部库名映射、缺失外键、多出外键和规则差异测试;数据库迁移专项测试、服务端全量测试、`go vet`、全量竞态检测、前端 ESLint 和生产构建均通过。
- **实机边界**:真实 MySQL、PostgreSQL、SQLite、Ubuntu、Windows、权限差异、网络中断、超大数据量和生产回滚仍需在目标环境复审,不能据此标记最终发布通过。
- **复审日期**:2026-08-31(本机自动化复审)。