# Web 账号安全复审

2026-10-03。Web 代码由子 agent 实现，本报告由根代理独立核验。当前限定范围未发现剩余已证实的阻断问题；不代表账号注销、实体设备或正式发布已完成。

## 验证来源

| 来源 | 范围 | 结果 |
| --- | --- | --- |
| 根独立新增 | 实际已安装 Better Auth 1.7.7 handler、独立内存数据库、捕获邮件回调 | 5 组通过：他人/当前会话不发送撤销；列表期间同用户 Cookie 替换拒绝；实际撤销后丢响应只 GET 确认；实际改邮箱后丢响应保留身份并只 GET 确认；官方退出后不再发送邮箱 POST |
| 根独立新增 | 实际 `consumer-auth.js`，DOM、身份接口与重载替代 | 修复前 1 组对照；当前 3 组通过：同用户换会话重载一次并清旧 pending、新页面稳定不循环、首次登录仍保留已确认的待恢复意图 |
| 根独立重跑作者用例 | 仓库全部 Web Node 用例 | 139/139，通过；其中新账号协议 9 组 |
| 根独立重跑作者用例 | 实际 Chrome 加载仓库 Web 页面，HTTP 和媒体替代 | 新账号页 15/15、既有登录 16/16，通过；没有页面 JS 错误 |

根新增脚本没有调用作者的官方框架场景或浏览器脚本：`work/account-write-root-review/web-api-check.mjs` 与 `web-auth-check.mjs`。另行重跑作者的 Chrome 脚本，不将相同 31 个场景记作新增独立设计场景。没有真实邮件、线上账号、安装包或生产 API 操作。

## 已关闭问题

**P2：同用户换登录会话时，旧播放活动没有失效。** 既有播放器和进度适配仅按 user ID 判断；原 auth 即使观察到 session ID 替换，只发普通身份变化事件，进度模块仍认为同一 owner，播放器也只更新追剧按钮。旧对话框可以关闭，但旧播放活动仍保留。

根独立使用实际 auth 模块复现：从 session-one 变为 session-two，user 不变，身份代数变化，但 reload 事件和实际重载调用都是 0，旧 pending 保留。作者按根复核意见只改既有 auth：真实非空 session ID 被替换时，清旧 pending、调用已有 `reloadIdentity`。已有进度模块收到 reload 事件关闭 reporter，整页重载释放旧媒体；没有新增状态存储或改变直连/转发策略。

同一根用例修复后：reload 事件 1、重载调用 1、旧 pending 删除。新页面读到同一新会话不循环；首次登录不提前删除待恢复追剧/筛选操作。实际 Chrome 还确认换会话后导航数从 1 到 2，旧对话框和 pending 清空，后续同会话刷新仍为 2；更换邮箱的 viewer 同步期间换会话也只重载一次。

## 核对的行为

- 官方同源 HTTP 适配；权威 GET 禁用 Cookie 缓存与续期，核对 origin、用户、真实 session ID 和既有请求代数。
- 会话 token 只在弹窗内存用于官方撤销，未进入 DOM、属性、导航或浏览器存储。OTP 提交和离开页面后清理。
- 撤销 200 和邮箱提交 200 都不直接算成功；真实 GET 结果才确认。未知结果留在“重新核对”，不自动重复 POST。
- 新邮箱需原、新两次官方验证码。最终邮箱和 viewer 同步成功才显示完成；桥接失败仍保留 GET 恢复步骤。
- 中英文原生表单、Enter/Escape、焦点和至少 44px 控制通过；手机与桌面无横向溢出。截图是接口替代下的真实代码画面。
- 首次身份加载的终态不触发无限重载；原登录的已授权 pending 恢复继续使用现有机制。

## 边界

实际框架证据采用内存数据库，后端 PostgreSQL 的撤销写入竞态另见[在途写入复验](dramivio-account-session-write-fence.md)。本报告不把合成媒体当成实际渠道播放，也未验证 Safari、原生系统凭据、服务端账号永久删除、离线许可或真实邮件送达。

完整功能与作者验证见[Web 实现交接](dramivio-web-account-implementation.md)。App 的独立复审见[App 报告](dramivio-app-account-review.md)，验证来源分别保留。
