# COPPA 儿童保护与数据安全措施实施方案 **文档状态**: 草案 **创建日期**: 2026-04-17 **关联文档**: `docs/discussions/legal-compliance-us.md` 第七点、第八点 --- ## 一、背景与法律要求 ### 1.1 COPPA(儿童在线隐私保护法)要求 针对 13 岁以下用户,COPPA 要求: 1. **年龄验证**:必须采取合理措施验证用户年龄 2. **禁止收集**:不得收集 13 岁以下儿童个人信息 3. **隐私政策披露**:隐私政策必须明确说明不面向 13 岁以下用户 ### 1.2 CCPA/CPRA 数据安全要求 针对数据安全措施,隐私政策声明的安全措施必须真实实施: 1. **加密措施**:AES-256 数据加密、TLS 1.3 传输加密 2. **访问控制**:基于角色的访问控制(RBAC) 3. **监控审计**:审计日志 4. **泄露响应**:72 小时内通知机制 --- ## 二、当前系统现状分析 ### 2.1 用户认证流程 **现有实现**: - 认证方式:邮箱 + OTP(验证码)登录 - 认证提供方:Supabase Auth - 用户表:`auth.users`(Supabase 内置) - 扩展表:`public.profiles` **缺失项**: - ❌ 无年龄验证机制 ### 2.2 数据安全现状 **已有措施**: - ✅ Supabase Auth 内置 JWT 认证(HS256 签名) - ✅ 数据库 RLS(Row Level Security)策略 - `anon`/`authenticated` 角色被禁止访问业务表 - ✅ 日志敏感字段过滤(`core/config/settings.py` 的 `log_sensitive_fields`) - ✅ 软删除机制(`SoftDeleteMixin`) - ✅ 环境变量管理敏感配置 - ✅ Supabase 托管服务默认启用 HTTPS + 数据库加密 **Supabase 安全特性**: - 传输加密:所有 API 请求强制 HTTPS - 静态数据加密:PostgreSQL 透明数据加密(TDE) - 密码哈希:bcrypt 自动处理 - JWT 签名:HS256 算法 - RLS 策略:细粒度行级访问控制 --- ## 三、第七点:COPPA 儿童保护实施方案 ### 3.1 年龄验证机制 **重要澄清:Apple 没有直接的年龄验证 API** Apple 不提供可以获取用户具体年龄的 API。Apple 的年龄保护机制是通过以下方式实现的: #### 方案一:App Store 年龄分级(推荐 - 主要防护) **原理**:在 App Store Connect 中设置年龄分级,App Store 会自动阻止不符合年龄要求的用户下载。 **实施步骤**: 1. **登录 App Store Connect** 2. **选择应用 → "App 信息" → "年龄分级"** 3. **填写年龄分级问卷** 4. **设置年龄分级为 12+(或更高)** **年龄分级说明**: - **4+**:无 objectionable 内容 - **9+**:可能包含轻微的卡通暴力/幻想暴力 - **12+**:可能包含轻微的现实暴力/偶尔粗俗幽默/轻微模拟赌博 - **17+**:可能包含频繁/强烈的现实暴力/成人内容 **设置 12+ 的效果**: - App Store 会阻止 12 岁以下用户下载应用 - 家长控制功能会根据年龄分级限制儿童访问 **优点**: - ✅ 零成本,无需开发 - ✅ Apple 系统级保护 - ✅ 自动生效 **缺点**: - ⚠️ 仅防止下载,不能防止借用他人设备/账号 - ⚠️ 用户可能谎报年龄注册 Apple ID #### 方案二:应用内年龄声明(推荐 - 辅助防护) **原理**:在注册流程中添加年龄确认 checkbox 或弹窗。 **实施步骤**: 1. **前端修改**: ```dart // apps/lib/features/auth/presentation/screens/login_screen.dart // 在登录界面添加年龄确认 checkbox Row( children: [ Checkbox( value: _ageConfirmed, onChanged: (value) { setState(() { _ageConfirmed = value ?? false; }); }, ), Expanded( child: Text( l10n.ageConfirmationText, // "我已年满 13 岁" style: Theme.of(context).textTheme.bodySmall, ), ), ], ), ``` 2. **后端验证**: ```python # backend/src/v1/auth/schemas.py class EmailSessionCreateRequest(BaseModel): model_config = ConfigDict(extra="forbid") email: str = Field(pattern=SUPABASE_EMAIL_PATTERN) token: str = Field(min_length=6, max_length=6) age_confirmed: bool = Field( default=False, description="用户确认年满 13 岁" ) ``` ```python # backend/src/v1/auth/service.py async def create_email_session( self, request: EmailSessionCreateRequest ) -> SessionResponse: if not request.age_confirmed: raise AgeConfirmationRequiredError( detail="User must confirm age 13 or older to register" ) return await self._gateway.create_email_session(request) ``` 3. **数据库记录**: ```sql -- 在 profiles 表添加年龄确认字段 ALTER TABLE profiles ADD COLUMN age_confirmed_at timestamptz, ADD COLUMN age_confirmation_method varchar(20) DEFAULT 'self_declaration'; ``` 4. **本地化文本**: ```json // apps/lib/l10n/app_zh.arb { "ageConfirmationText": "我确认已年满 13 岁,或已获得家长/监护人同意使用本应用", "@ageConfirmationText": { "description": "年龄确认声明文本" } } ``` ```json // apps/lib/l10n/app_en.arb { "ageConfirmationText": "I confirm that I am 13 years of age or older, or have parental/guardian consent to use this app" } ``` **优点**: - ✅ 实现简单 - ✅ 满足 COPPA 最低合规要求 - ✅ 记录用户确认时间和方式 **缺点**: - ⚠️ 依赖用户自我声明 - ⚠️ 用户可能撒谎 #### 推荐方案:App Store 年龄分级 + 应用内年龄声明 **组合使用**: 1. **App Store 年龄分级设置为 12+**(主要防护) 2. **应用内注册时要求年龄确认**(辅助防护 + 合规记录) **理由**: - App Store 分级阻止儿童下载(系统级保护) - 应用内确认作为法律合规证据(用户明确声明) - 双重保护,满足 COPPA 要求 ### 3.2 隐私政策更新 **需要添加的内容**: ```markdown ## 儿童隐私保护 本应用不面向 13 岁以下儿童。 **年龄限制**: - 您必须年满 13 岁才能使用本应用 - 本应用在 App Store 的年龄分级为 12+,系统会阻止 12 岁以下用户下载 - 注册时,您需要确认年满 13 岁 **数据收集**: - 我们不会故意收集 13 岁以下儿童的个人信息 - 如果我们发现在未经许可的情况下收集了儿童的个人信息,我们将采取措施删除相关信息 **联系我们**: 如果您认为我们可能收集了儿童的个人信息,请联系 privacy@xunmee.com。 ``` --- ## 四、第八点:数据安全措施实施方案 ### 4.1 加密措施 #### 4.1.1 传输加密(TLS) **当前状态**:✅ 已满足 - Supabase 托管服务默认强制 HTTPS - 所有 API 请求使用 TLS 1.2/1.3 加密 - 无需额外配置 **验证方式**: - 使用 SSL Labs 测试:https://www.ssllabs.com/ssltest/ - 目标评级:A 或 A+ #### 4.1.2 静态数据加密(AES-256) **当前状态**:✅ 已满足 - Supabase PostgreSQL 默认启用透明数据加密(TDE) - Supabase Storage 默认启用服务端加密 - 无需额外配置 **用户输入是否需要加密字段存储?** **回答:不需要** 理由: 1. **当前数据类型不敏感**: - 用户输入主要是占卜问题、起卦数据 - 不涉及身份证、信用卡、医疗健康等高敏感信息 - 数据库已有 RLS 保护,只有 `service_role` 可访问 2. **加密成本**: - 应用层加密会增加查询复杂度(无法索引、无法搜索) - 增加开发和维护成本 - 性能开销 3. **已有保护措施**: - 数据库 TDE(透明数据加密) - RLS 策略 - JWT 认证 - 传输加密 ### 4.2 访问控制 #### 4.2.1 基于角色的访问控制(RBAC) **当前实现**:✅ 已满足 - 数据库 RLS 策略:`anon`/`authenticated` 角色被禁止访问业务表 - 后端 Service 层强制校验 `owner_id` - 只有 `service_role` 可以访问业务数据 **RLS 策略示例**(来自 `20260411_0001_initial_llm_schema.py`): ```sql -- 所有业务表启用 RLS ALTER TABLE table_name ENABLE ROW LEVEL SECURITY; -- 禁止 anon 和 authenticated 角色访问 CREATE POLICY anon_select_table_name ON table_name FOR SELECT TO anon USING (false); CREATE POLICY authenticated_select_table_name ON table_name FOR SELECT TO authenticated USING (false); -- 只有 service_role 可以访问(后端使用 service_role key) ``` #### 4.2.2 Supabase 是否拒绝匿名用户登录? **回答:是的** - Supabase Auth 默认要求认证 - 未登录用户只有 `anon` 角色 - RLS 策略禁止 `anon` 角色访问业务表 - 后端 API 依赖 JWT 验证用户身份 **验证流程**: ```python # backend/src/v1/auth/dependencies.py async def get_current_user( authorization: str | None = Header(default=None) ) -> AuthUser: if not authorization: raise UnauthenticatedError() # 拒绝匿名访问 # 验证 JWT user = await verify_jwt(authorization) return user ``` ### 4.3 审计日志 #### 4.3.1 当前 points_audit_ledger 是否满足要求? **回答:部分满足** **points_audit_ledger 现状**: - ✅ 记录积分变更审计 - ✅ 包含 `user_id_snapshot`、`user_email_snapshot` - ✅ 包含 `event_id`、`change_type`、`amount` - ✅ 包含时间戳 `created_at` - ❌ 只记录积分相关操作,不记录其他操作 **不满足的审计需求**: - ❌ 用户登录/登出 - ❌ 账户删除 - ❌ 个人信息修改 - ❌ 数据导出请求 **建议:扩展审计日志系统** ```sql -- 创建通用审计日志表 CREATE TABLE audit_logs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid REFERENCES auth.users(id) ON DELETE SET NULL, action varchar(50) NOT NULL, resource_type varchar(50) NOT NULL, resource_id uuid, old_values jsonb, new_values jsonb, ip_address inet, user_agent text, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX ix_audit_logs_user_id ON audit_logs(user_id); CREATE INDEX ix_audit_logs_action ON audit_logs(action); CREATE INDEX ix_audit_logs_created_at ON audit_logs(created_at); COMMENT ON TABLE audit_logs IS '通用审计日志:记录用户关键操作'; ``` **审计事件类型**: ```python # backend/src/core/audit/events.py class AuditAction(str, Enum): # 认证相关 USER_LOGIN = "user.login" USER_LOGOUT = "user.logout" USER_DELETE = "user.delete" # 数据操作 PROFILE_UPDATE = "profile.update" DATA_EXPORT = "data.export" # 积分相关(已有 points_audit_ledger) POINTS_CHANGE = "points.change" ``` **实施优先级**: - P1:创建 `audit_logs` 表 - P2:记录用户登录/登出 - P3:记录账户删除 - P4:记录个人信息修改 ### 4.4 数据泄露响应 #### 4.4.1 响应流程文档 **文档位置**:`docs/security/incident-response.md` **响应时间线**: | 阶段 | 时间 | 行动 | |------|------|------| | 发现 | T+0 | 确认泄露范围和影响 | | 评估 | T+1h | 确定严重等级(低/中/高/严重)| | 控制 | T+4h | 阻止泄露继续,保留证据 | | 通知 | T+72h | 通知受影响用户(法律要求)| | 修复 | T+7d | 修复漏洞,加强防护 | | 复盘 | T+14d | 总结经验,更新流程 | --- ## 五、实施优先级与时间线 ### 5.1 优先级矩阵 | 优先级 | 项目 | 预计工时 | 合规必要性 | 状态 | |--------|------|----------|------------|------| | P0 | App Store 年龄分级设置 | 0.5 天 | 必须 | 待实施 | | P0 | 隐私政策更新 | 0.5 天 | 必须 | 待实施 | | P1 | 应用内年龄确认 | 1-2 天 | 必须 | 待实施 | | P2 | 通用审计日志表 | 2 天 | 推荐 | 待实施 | | P3 | 泄露响应流程文档 | 1 天 | 推荐 | 待实施 | ### 5.2 MVP 上线前必须完成 1. ✅ App Store 年龄分级设置为 12+ 2. ✅ 隐私政策更新(添加儿童隐私章节) 3. ✅ 应用内年龄确认(checkbox + 后端记录) ### 5.3 上线后 3 个月内完成 1. 通用审计日志系统 2. 泄露响应流程文档 --- ## 六、验收标准 ### 6.1 COPPA 合规 - [ ] App Store 年龄分级设置为 12+(或更高) - [ ] 应用内注册时要求年龄确认 - [ ] 后端记录年龄确认时间和方式 - [ ] 隐私政策明确说明不面向 13 岁以下用户 ### 6.2 数据安全 - [ ] 所有 API 强制 HTTPS(Supabase 默认) - [ ] SSL Labs 评级 A 或以上 - [ ] 数据库 RLS 策略正确配置 - [ ] 审计日志记录关键操作 - [ ] 有书面泄露响应流程 --- ## 七、关键决策记录 ### 7.1 年龄验证方案 **决策**:App Store 年龄分级 + 应用内年龄声明 **理由**: 1. Apple 没有直接的年龄验证 API 2. App Store 年龄分级(12+)可阻止儿童下载(系统级保护) 3. 应用内年龄确认作为法律合规证据 4. 零额外成本 **实施方式**: - **主要防护**:App Store Connect 设置年龄分级为 12+ - **辅助防护**:注册时 checkbox 确认年满 13 岁 + 后端记录 ### 7.2 数据加密 **决策**:依赖 Supabase 默认加密,不额外应用层加密 **理由**: 1. 当前数据类型不敏感(占卜问题、起卦数据) 2. Supabase 已提供 TDE + HTTPS 3. 应用层加密成本高、收益低 ### 7.3 审计日志 **决策**:扩展通用审计日志表 **理由**: 1. `points_audit_ledger` 只记录积分变更 2. 需要记录用户登录、删除等操作 3. 满足 CCPA/CPRA 审计要求 --- ## 八、参考资源 ### 8.1 法律法规 - [COPPA 官方指南](https://www.ftc.gov/business-guidance/privacy-security/childrens-privacy) - [CCPA 官方文本](https://oag.ca.gov/privacy/ccpa) ### 8.2 技术参考 - [App Store 年龄分级](https://developer.apple.com/help/app-store-connect/reference/age-ratings/) - [Supabase Auth 文档](https://supabase.com/docs/guides/auth) - [Supabase RLS 文档](https://supabase.com/docs/guides/auth/row-level-security) --- **下一步行动**: 1. 在 App Store Connect 设置年龄分级为 12+ 2. 更新隐私政策文档 3. 实现应用内年龄确认(前端 checkbox + 后端记录)