startup-plan/技术设计/api-server/已完成/[已完成]-安全规范.md
WangDL fe608da385 docs: 重构技术设计目录结构 + 更新待完成清单
- 文档从扁平结构迁移至分类目录 (api-server/ios-projects/web-projects/长期规划)
- 更新总待完成清单 (B1-B6 全部完成, I1-I7 全部完成)
- 新增后端实现状态、已实现功能汇总等已完成文档
- 新增 iOS 功能需求清单、架构设计、差距分析等文档
- 清理旧版未维护文档

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 19:08:59 +08:00

5.4 KiB
Raw Blame History

知习 api-server 安全基线

v0.1 安全设计文档。本后端存储用户资料、知识库、上传文件、主动回忆回答、AI 分析结果和学习记录,第一版必须建立基础安全边界。


1. 全局安全中间件

措施 实现 文件
helmet app.use(helmet()) 设置安全 HTTP 头 src/main.ts
CORS 仅允许配置域名。生产环境仅允许 longde.cloud src/main.ts
body size limit JSON 请求体最大 10MB src/main.ts
异常过滤 生产环境不返回 stack trace src/common/filters/global-exception.filter.ts

2. 认证与 Token

JWT

  • accessToken: JWT1 小时过期
  • refreshToken: 128 位随机 hex入库只存 SHA-256 hash
  • logout 时 revokedAt = now() 撤销所有 refresh token
  • /users/me 及其所有子路由强制 @UseGuards(JwtAuthGuard)
POST /auth/apple   → 返回 accessToken + refreshToken
POST /auth/refresh → 消耗旧 refreshToken发放新 token pairrotation
POST /auth/logout  → 撤销该用户所有 refresh token

存储安全

refresh_tokens.tokenHash = SHA-256(实际 token)
数据库中永远不存明文 refreshToken

3. 权限与越权防护

角色体系

角色 权限范围
USER 访问自己的资源
ADMIN 用户管理、数据查看、反馈管理
SUPER_ADMIN 全部权限(含 ADMIN

角色层级:SUPER_ADMINADMINUSER

实现文件:

文件 作用
common/types/role.enum.ts Role 枚举 + ROLE_HIERARCHY + hasRole()
common/decorators/roles.decorator.ts @Roles(Role.ADMIN) 装饰器
common/guards/roles.guard.ts 全局 RolesGuard,校验用户 role

用法:

@Get('admin/users')
@Roles(Role.ADMIN)  // 仅 ADMIN 及以上可访问
async listUsers() {}

RolesGuard 已注册为全局 APP_GUARD(在 JwtAuthGuard 之后执行),默认不需要特殊角色即可访问的路由不加 @Roles() 即可。

资源归属校验

所有用户资源操作必须校验 userId 归属:

// src/common/utils/security.util.ts
export async function findByIdAndUserId(delegate, id, userId, resourceName)
export function ensureOwnership(record, userId, resourceName)

需校验的资源

资源 校验字段
KnowledgeBase userId
KnowledgeItem userId
LearningSession userId
ActiveRecallAnswer userId
AiAnalysisJob userId
AiAnalysisResult userId
FocusItem userId
ReviewCard userId
ReviewLog userId
DocumentImport userId

4. 参数校验

  • 全局 StrictValidationPipe:
    • whitelist: true — 自动剥离未声明字段
    • forbidNonWhitelisted: true — 未知字段返回 400
    • 字符串字段最大长度 5000 字符
  • 分页 DTO: page≥1, limit 1-100

5. 限流Redis

场景 Key 限制
登录 rate:ip:{ip}:login:{date} 20次/IP/天
反馈 rate:ip:{ip}:feedback:hourly 5次/IP/时
AI 分析 rate:user:{userId}:ai:daily:{date} 50次/用户/天
文件上传 rate:user:{userId}:upload:hourly 10次/用户/时

实现: src/common/utils/rate-limit.service.ts


6. 文件上传安全

措施 说明
类型白名单 PDF, Word, Excel, 纯文本, Markdown, CSV, PNG, JPEG, WebP
大小限制 最大 20MB
随机文件名 sanitizeFilename() 生成随机 key不信任用户原始文件名
默认私有 所有文件默认私有访问
路径隔离 users/{userId}/...

7. Redis 安全使用

  • 不存核心业务结果(用户资料/知识点/AI分析结果等必须在 MySQL
  • 队列任务只存 jobId/userId 等引用 ID
  • 所有临时 key 必须设置 TTL
  • 防重复提交锁必须有 TTL解锁校验 token
  • 不在 Redis 中存 token 明文

8. COS 安全使用

  • Bucket 默认私有读写
  • 后端不向前端暴露 SecretId/SecretKey
  • 下载私有文件通过签名 URL
  • 上传路径按 users/{userId}/{randomKey} 组织
  • 预留临时上传 URLSTS机制

9. Swagger 安全

  • 开发环境默认开启
  • 生产环境默认关闭
  • 生产环境如需开启,必须配置 Basic AuthSWAGGER_USER/SWAGGER_PASSWORD
  • 生产环境手动设置 ENABLE_SWAGGER=true

10. 数据库安全

  • 不使用 root 连接业务
  • 业务账号 zhixi_user 仅需 SELECT/INSERT/UPDATE/DELETE
  • 迁移账号和业务账号分离(prisma db push 与运行时连接帐号可不同)
  • 数据库自动备份建议: mysqldump zhixi | gzip > backup-$(date +%Y%m%d).sql.gz

日志中禁止打印

DATABASE_URL含密码
JWT_SECRET
AI_API_KEY
COS SecretKey
用户完整 refreshToken
用户上传文件的完整内容
Authorization header

11. 安全检查清单

  • helmet 已启用
  • CORS 仅允许白名单域名
  • JWT + refresh token rotation + hash 存储
  • logout 撤销 refresh token
  • 所有用户数据接口需要认证
  • 资源所有权校验工具已就绪
  • StrictValidationPipe 全局启用whitelist + forbidNonWhitelisted
  • Redis 限流已实现
  • 文件类型/大小白名单
  • 全局异常过滤器生产环境不暴露 stack trace
  • Swagger 生产环境默认关闭
  • 敏感信息不在日志中打印原则已确立