Files
lehuo/docs/06_SECURITY_PRIVACY.md
T

315 lines
12 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` 志愿者可报名;活动行锁、有效报名计数和数据库唯一约束共同防止并发超额与重复报名。
- 报名审核、取消、签到、缺席和服务分钟均写不可变历史与审计。服务分钟只允许对已通过、已签到且活动已完成的记录核定,且不得超过活动持续时间。
- 公益项目和活动公开接口只返回公开状态内容,不返回志愿者名单或服务对象资料。封面未配置对象存储时不生成假地址或假成功。
## 财务公开富文本安全(2026-08-13)
- DOCX 仅在管理端浏览器转换,不由 API 执行或解析 Office 宏;首版不持久化原 Word 文件。
- 管理端导入和预览先经 DOMPurify 清洗,API 保存前再次使用独立白名单清洗;禁止脚本、事件属性、iframe、非 HTTPS 链接和非 HTTPS 图片。
- 只有管理员与超级管理员默认获得财务公开查看、编辑和发布权限;公众只能访问已发布文档。
- 已发布文档禁止原地覆盖编辑,归档后不可公开;发布和归档写入不可变历史与审计。
## 旧物善用隐私与文件安全(2026-08-15)
- 联系人、手机号、上门取件地址、物品描述、驳回原因和内部轨迹备注加密保存;手机号查询索引使用独立域 HMAC。
- 捐赠图片存入私有对象前缀,上传前校验 MIME、扩展名、大小,确认时校验对象元数据;不返回永久公开 URL。
- 本人捐赠与图片下载执行资源归属校验;管理端详情和图片 URL 获取要求独立权限并写审计。列表只显示脱敏手机号,不记录地址或描述到日志/审计 metadata。
- 公益编号含随机片段,所有外部资源使用随机 UUID,不暴露数据库自增 ID。
- 物资台账的内部备注继续加密;关联受助对象只返回随机公开 ID,不在导出、打印或小程序溯源中返回姓名、手机号、地址或服务描述。
- Excel 只在管理端浏览器解析和生成,API 不执行宏、不保存原工作簿;公式单元格拒绝导入。固定模板最多 500 行,服务端再次执行白名单、类型和状态校验,整批事务失败回滚。
- 台账导出使用独立权限并写审计,最多 5000 条且默认最小化;小程序不公开内部仓位和操作人,仅公开出库/落地地区。
- 电子证书收件人称呼、撤销原因和积分人工校正原因加密保存;管理端证书列表统一显示“爱心用户”,本人登录后才可查看自己的证书正文。
- 证书公开标识和证书编号均不可预测,且不提供无需登录的证书核验接口;本人证书详情执行资源归属校验,防止 IDOR/BOLA。
- 证书模板是服务端校验并转义展示的纯文本,仅允许批准占位符,不执行 HTML;每张证书保存不可变快照,避免模板修改篡改历史凭证。
- 规则激活、自动签发、补发、撤销、积分获得和人工校正均审计;积分账户使用行锁/revision,流水只追加,余额不得为负。
## 发布前配置与入口加固(2026-08-15)
- 后台 Cookie 继续使用 HttpOnly 与 SameSite=Strict;仅在启用 HTTPS Secure Cookie 时附加 Partitioned 属性,避免本地非 HTTPS 开发环境丢失会话。
- 后台写请求优先精确校验 Origin;仅在请求没有 Origin 时解析 Referer 并比较其完整 origin,不允许前缀匹配或通配来源。
- 审计写入前防御性剥离会话原始 token;台账导出审计保留查询关键词,但不记录导出内容、联系人或地址。
- 私有对象上传签名有效期缩短为 120 秒;数据库启动日志只报告已完成 migration,待部署检查仍由发布流程中的 `prisma migrate deploy` 负责。
- 小程序开发版允许连接 `localhost/127.0.0.1` 回环 API;体验版和正式版拒绝回环、`0.0.0.0` 与非 HTTPS 地址。提审前仍须替换为微信合法 HTTPS 域名。