- 文档从扁平结构迁移至分类目录 (api-server/ios-projects/web-projects/长期规划) - 更新总待完成清单 (B1-B6 全部完成, I1-I7 全部完成) - 新增后端实现状态、已实现功能汇总等已完成文档 - 新增 iOS 功能需求清单、架构设计、差距分析等文档 - 清理旧版未维护文档 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.4 KiB
5.4 KiB
知习 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: JWT,1 小时过期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 pair(rotation)
POST /auth/logout → 撤销该用户所有 refresh token
存储安全
refresh_tokens.tokenHash = SHA-256(实际 token)
数据库中永远不存明文 refreshToken
3. 权限与越权防护
角色体系
| 角色 | 权限范围 |
|---|---|
USER |
访问自己的资源 |
ADMIN |
用户管理、数据查看、反馈管理 |
SUPER_ADMIN |
全部权限(含 ADMIN) |
角色层级:SUPER_ADMIN ⊃ ADMIN ⊃ USER
实现文件:
| 文件 | 作用 |
|---|---|
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}组织 - 预留临时上传 URL(STS)机制
9. Swagger 安全
- 开发环境默认开启
- 生产环境默认关闭
- 生产环境如需开启,必须配置 Basic Auth(
SWAGGER_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 生产环境默认关闭
- 敏感信息不在日志中打印原则已确立