Files
lehuo/docs/06_SECURITY_PRIVACY.md
T
2026-08-12 14:35:12 +08:00

286 lines
8.4 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.
# 06 安全、隐私与合规基线
## Phase 6–7 落地补充(2026-08-12)
内容未经审核不得公开;服务故事发布必须显式确认脱敏,拒绝脚本与事件处理器标记,客户端正文不作为 HTML 执行。审核意见加密,历史只追加。站内消息按 `user_id` 强制隔离;微信凭据、openid、token 和消息正文不得写日志。
统计导出仅为汇总事实,不包含手机号、地址、儿童记录或服务描述,要求独立权限并记录范围和 `masked=true` 审计。管理 API 权限扫描、官方 High 级依赖审计、密钥特征扫描和 CodeQL 工作流已建立。
> 本文是工程基线,不替代正式法律意见。上线前应由运营主体根据实际收集信息、服务范围和微信平台规则完成隐私政策、用户协议和必要合规审核。
## 1. 数据分类
### L1 公开
公益项目公开介绍、公开活动、组织联系方式。
### L2 内部
普通内部运营信息、非敏感统计。
### L3 敏感业务
服务申请详情、家庭困难、住址、联系方式、服务记录。
### L4 高敏感
儿童个人信息、监护信息、健康/残疾相关详细信息、证件、特殊风险记录。
默认 L3/L4 不得公开。
---
## 2. 最小收集
每个字段进入系统前必须回答:
- 为什么需要?
- 不收集是否仍能提供服务?
- 保存多久?
- 谁能看?
- 是否可脱敏?
- 是否真的需要精确地址/证件?
没有明确目的不得新增收集项。
---
## 3. 未成年人
不满 14 周岁相关个人信息必须采用更严格保护:
- 明确监护人角色;
- 记录同意;
- 限制访问人员;
- 默认不公开案例身份;
- 不允许志愿者看到与任务无关的信息;
- 定期审计访问;
- 允许合法更正、删除/撤回等请求按规则处理。
---
## 4. 加密
传输:
- 全站 HTTPS/TLS。
存储:
- 云盘/数据库基础加密按环境开启;
- 极敏感字段应用层加密;
- 密钥不与数据库同库;
- 密钥通过云 KMS/Secret Manager 或安全环境变量管理。
密码:
- Argon2id 或 bcrypt 强哈希;
- 禁止 MD5/SHA1 直接存密码。
---
## 5. 手机号和地址
显示:
- 普通列表默认脱敏,例如 138****1234;
- 详细查看必须权限。
查询:
- 如需精确匹配可存 HMAC/hash 辅助索引;
- 明文/密文设计需避免为搜索而降低保护。
---
## 6. 日志
禁止记录:
- 身份证完整号码
- 完整手机号
- openid
- access token
- session token
- 详细住址
- 儿童档案内容
- 服务描述全文
- 上传文件原文
允许:
- public_id
- requestId
- action code
- status
- 耗时
- 脱敏 actor 信息
---
## 7. OWASP 基线
必须防:
- SQL injection
- XSS
- CSRF(后台 Cookie 认证时)
- SSRF
- IDOR/BOLA
- path traversal
- unrestricted upload
- mass assignment
- brute force
- credential stuffing
- rate abuse
- insecure direct object reference
每个资源 API 后端都做授权,不相信前端按钮是否隐藏。
---
## 8. 限流
按场景设置:
- 登录
- 验证码
- 服务申请
- 文件上传
- 搜索
- 导出
- 微信登录 exchange
不能只按 IP,必要时组合 user/device/IP。
---
## 9. 审计事件
至少审计:
- 登录成功/失败
- 角色权限修改
- 查看高敏感档案
- 新增/修改服务对象
- 服务申请状态变化
- 工单分配
- 服务记录修改
- 导出
- 删除/归档
- 隐私同意变化
- 系统配置变化
---
## 10. 数据保留
不要永久保留所有数据。
必须由运营方定义:
- 服务档案保存期限;
- 未成年人数据期限;
- 日志期限;
- 导出文件期限;
- 已取消授权后的处理;
- 项目验收要求。
程序上把保留策略做成可配置任务,而不是散落代码。
---
## 11. 权限矩阵核心原则
### Volunteer
只能:
- 看自己被分配任务;
- 看完成服务所需的最小信息;
- 提交服务记录;
不能:
- 搜索全体服务对象;
- 导出名单;
- 看完整历史档案。
### Staff
按负责范围访问。
### Project Manager
按项目访问。
### Admin
跨项目业务管理,但高敏感导出仍可要求额外权限。
### Super Admin
系统维护,不等于默认业务数据无限查看;高敏感读取同样审计。
---
## 12. 安全发布门禁
上线前必须:
- npm/pnpm dependency audit
- secret scan
- SAST 基础扫描
- API 权限测试
- 越权测试
- 文件上传测试
- SQL 注入/XSS 测试
- 弱密码/暴力登录测试
- 备份恢复测试
- staging 冒烟测试
发现 Critical/High 安全问题不得上线。
---
## 13. Phase 1 已落地基线
- 后台密码使用 Argon2id;未知用户名执行 dummy verify,降低账号枚举时序差异。
- 连续 5 次密码失败后锁定 15 分钟;登录和微信 code exchange 均按入口限流。
- 后台会话只通过 HttpOnly、SameSite=Strict Cookie 传递,并校验后台 Origin;小程序使用短期 Bearer 会话。
- 数据库只保存会话 token 的带 pepper HMAC;重新登录轮换旧会话,注销、过期、停用账户立即拒绝。
- openid/unionid 使用 AES-256-GCM 加密并用域隔离 HMAC 建查询索引;密钥只来自环境变量。
- 默认认证、权限 Guard 与 resource policy 基线已建立;API 不暴露内部 bigint ID。
- 登录成功/失败、注销和角色变更写入只追加审计;审计元数据拒绝敏感字段名,IP 只保存 HMAC。
- 后台 CORS 仅允许配置的精确 Origin;输入使用白名单校验并拒绝额外字段。
---
## 14. Phase 2 已落地基线
- 服务对象称呼、联系电话和需求描述按 L3 数据使用 AES-256-GCM 应用层加密;手机号查询索引和请求幂等指纹使用密钥化、域隔离 HMAC-SHA-256。
- 申请列表默认只返回脱敏手机号;后台详情仅向 ADMIN 渠道且具备 `applications:read` 的人员开放,每次查看均写只追加审计。
- 小程序申请详情除随机 `publicId` 外继续执行申请人资源归属检查,防止 IDOR/BOLA;后台角色不能通过微信入口读取他人资源。
- DTO 白名单拒绝额外字段,分页限制为 1–100,手机号和内容长度由服务端验证;提交入口每分钟最多 5 次。
- 隐私同意、申请、初始状态历史和审计在单一数据库事务中提交;幂等由数据库唯一约束处理并覆盖并发重复提交。
- 小程序不把服务申请敏感草稿写入本地持久化;网络失败时只在当前页面内保留内容和同一请求标识,方便安全重试。
---
## 15. Phase 4 已落地基线
- 回访原因、结果说明、服务完成总结和回访计划按 L3 数据使用 AES-256-GCM 加密;列表不返回原因或结果正文。
- `follow_ups:read/manage/complete` 只授予工作人员及以上角色,志愿者不获得回访结果权限;无全量资源权限时只返回本人负责的任务。
- 服务完成和回访完成都只允许负责人写入,并使用客户端 UUID、请求指纹、状态条件更新和数据库唯一约束处理重试/并发。
- 继续帮扶必须记录新的明确授权;未成年人由监护人授权。新授权、新申请、原申请关闭、回访状态历史和审计在同一事务提交。
- 审计元数据仅记录随机公开标识、来源、结果枚举和是否创建继续申请,不记录回访正文、手机号、详细地址或服务描述。
---
## 16. Phase 5 已落地基线
- 志愿者姓名、手机号、可参与时间和审核备注按敏感数据加密;后台列表手机号默认脱敏,授权详情读取写审计,小程序只读取本人资料和报名。
- 首次申请和重新提交均记录版本化明确同意;已认证志愿者修改姓名或手机号后自动撤销志愿者角色并回到待审核,不能绕过身份复核。
- 项目负责人只访问本人负责的项目/关联活动;全量资源访问继续由已批准权限控制。无全量权限时禁止把其他项目工单转移进本人项目。
- 只有 `APPROVED` 志愿者可报名;活动行锁、有效报名计数和数据库唯一约束共同防止并发超额与重复报名。
- 报名审核、取消、签到、缺席和服务分钟均写不可变历史与审计。服务分钟只允许对已通过、已签到且活动已完成的记录核定,且不得超过活动持续时间。
- 公益项目和活动公开接口只返回公开状态内容,不返回志愿者名单或服务对象资料。封面未配置对象存储时不生成假地址或假成功。