# 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. 默认禁止 `