[B-10][high] 密码校验与写库不是原子的:重置、停用、删除期间的登录、改密、退出会越过管理员操作 #17

Closed
opened 2026-09-30 13:56:53 +08:00 by nixevol · 1 comment
Owner

编号:B-10 严重级:high 工作线:broker(internal/broker) + 身份、群、在线与认证基础(internal/app/identity、group、presence、internal/auth) 来源:审查 I-08
依赖:B-09 (#16)(同改 handleHello) 被依赖:无

结论与统一方案

采用审查 I-08 的方案:

  1. 登录写令牌改为 UPDATE ... WHERE id=? AND enabled=1 AND login_hash=?,影响 0 行按认证失败处理(不计锁定)。
  2. 端自己改登录密码同样带 login_hash、enabled 条件,影响 0 行返回 unauthorized。
  3. connState 记下本连接令牌的哈希,logout 改为 WHERE id=? AND session_hash=?,不误清同时登录的新设备的令牌。
  4. handleHello 在标在线前复读 enabled 与 session_hash,不匹配就断开。后台操作是"先提交再踢当前连接",之后才成为当前连接的会话,其 hello 一定晚于提交,能被这一步拦住。

改动文件

internal/broker/authn.go、session.go、broker.go;internal/app/identity/self.go。

与其他问题的交互 / 冲突说明

  • 第 4 点与 B-09 同在 handleHello,B-09 先合。
  • self.go 只改改密函数,身份线其他 issue 不动它。

验收与测试

  • 注入在 Verify 里阻塞的 HashPool,登录进行中执行重置、停用、删除,放行后登录被拒,session_hash 仍为空。
  • 改密版本同理,断言 login_hash 是管理员设的值。
  • logout 与新设备同时登录后,新令牌仍然有效。

问题明细(各区审查原文,证据含文件与行号)

以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。

[I-08] 密码校验和写库不是原子操作:重置、停用、删除期间的登录、改密、退出会越过管理员操作

  • 严重级:high
  • 分类:安全 / 并发
  • 现象与影响
    • (a) 密码登录的流程是:从读库拿 login_hash 和 enabled → argon2 校验(可能排队)→ 无条件执行 UPDATE … WHERE id=?,也不看影响行数。
      • 重置密码:后台先清令牌,再踢「当前连接」,但这次登录的会话还没建立,踢不到。登录随后写入新令牌、建立会话。反复用旧密码登录的人可以轻松赢得这个竞态,重置密码赶不走他。
      • 停用:停用后仍能建立会话,并留下一个重新启用后仍有效的令牌。
      • 删除:更新影响 0 行也返回成功,形成已删除编号的「幽灵连接」。之后同编号被重新开通或被他人自助注册,这个连接就会收到新端的消息,违反「新端不继承任何旧数据」。
    • (b) self.login_password 同样是先读再无条件 UPDATE,可能覆盖同时发生的管理员重置。
    • (c) logout 调用的 ClearSession 按编号无条件清空,会误清同时用密码登录的新设备的令牌。
  • 证据
    • 登录:broker/authn.go:112-145、195-238(见下方代码)。
    • logout 的清空:authn.go:240-260。
    • 改密:identity/self.go:134-188。
    • 重置:admin/endpoints_db.go:381-395 之后才踢线;踢线只踢当时的当前连接:broker/session.go:316-336。
	writeErr := l.DB.Queue.Do(ctx, func(tx *sql.Tx) error {
		_, e := tx.Exec(`
UPDATE endpoints
SET session_hash = ?, session_issued_at = ?, session_used_at = ?
WHERE id = ?`, hashHex, nowMs, nowMs, endpointID)
		return e
	})
  • 文档依据:PRD F02(152)、F01(136-137);DEVELOPMENT 7.1(618)「所有变更都用带条件的 UPDATE」。
  • 为何不是故意设计:没有相关偏差记录,属于先检查后使用(TOCTOU)的竞态。
  • 解决方案
    1. 登录写令牌改为 WHERE id=? AND enabled=1 AND login_hash=?,影响 0 行时返回认证失败,不计锁定。
    2. 改密同样加 login_hash 和 enabled 条件,影响 0 行时返回 unauthorized。
    3. connState 记下本连接令牌的哈希,logout 改为 WHERE id=? AND session_hash=?。
    4. handleHello 在标在线前复读 enabled 和 session_hash,不匹配就断开。后台操作是「先提交再踢当前连接」,踢线之后才成为当前连接的会话,其 hello 一定晚于提交,能被这一步拦住。
  • 改动文件:broker/authn.go、broker/session.go、broker/broker.go、identity/self.go。
  • 与其他模块的交互/冲突风险:hello 多一次读库,走读池,WAL 模式下不阻塞写;和 I-4 同在 handleHello,需要合并改。
  • 需补测试
    • 注入一个在 Verify 里阻塞的 HashPool,登录进行中执行重置、停用、删除,放行后登录被拒,session_hash 仍为空。
    • 改密版本同理,断言 login_hash 是管理员设的值。
    • logout 与新设备同时登录后,新令牌仍然有效。
  • 置信度:代码阅读确定。

复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。

**编号**:B-10 **严重级**:high **工作线**:broker(internal/broker) + 身份、群、在线与认证基础(internal/app/identity、group、presence、internal/auth) **来源**:审查 I-08 **依赖**:B-09 (#16)(同改 handleHello) **被依赖**:无 ### 结论与统一方案 采用审查 I-08 的方案: 1. 登录写令牌改为 `UPDATE ... WHERE id=? AND enabled=1 AND login_hash=?`,影响 0 行按认证失败处理(不计锁定)。 2. 端自己改登录密码同样带 `login_hash`、`enabled` 条件,影响 0 行返回 unauthorized。 3. connState 记下本连接令牌的哈希,logout 改为 `WHERE id=? AND session_hash=?`,不误清同时登录的新设备的令牌。 4. `handleHello` 在标在线前复读 `enabled` 与 `session_hash`,不匹配就断开。后台操作是"先提交再踢当前连接",之后才成为当前连接的会话,其 hello 一定晚于提交,能被这一步拦住。 ### 改动文件 `internal/broker/authn.go`、`session.go`、`broker.go`;`internal/app/identity/self.go`。 ### 与其他问题的交互 / 冲突说明 - 第 4 点与 B-09 同在 `handleHello`,B-09 先合。 - `self.go` 只改改密函数,身份线其他 issue 不动它。 ### 验收与测试 - 注入在 Verify 里阻塞的 HashPool,登录进行中执行重置、停用、删除,放行后登录被拒,`session_hash` 仍为空。 - 改密版本同理,断言 `login_hash` 是管理员设的值。 - logout 与新设备同时登录后,新令牌仍然有效。 --- ### 问题明细(各区审查原文,证据含文件与行号) > 以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。**解决方案以本 issue 上方的"结论与统一方案"为准**;原文里的方案与之不一致时,按上方执行。 #### [I-08] 密码校验和写库不是原子操作:重置、停用、删除期间的登录、改密、退出会越过管理员操作 - **严重级**:high - **分类**:安全 / 并发 - **现象与影响** - (a) 密码登录的流程是:从读库拿 `login_hash` 和 `enabled` → argon2 校验(可能排队)→ 无条件执行 `UPDATE … WHERE id=?`,也不看影响行数。 - **重置密码**:后台先清令牌,再踢「当前连接」,但这次登录的会话还没建立,踢不到。登录随后写入新令牌、建立会话。反复用旧密码登录的人可以轻松赢得这个竞态,重置密码赶不走他。 - **停用**:停用后仍能建立会话,并留下一个重新启用后仍有效的令牌。 - **删除**:更新影响 0 行也返回成功,形成已删除编号的「幽灵连接」。之后同编号被重新开通或被他人自助注册,这个连接就会收到新端的消息,违反「新端不继承任何旧数据」。 - (b) `self.login_password` 同样是先读再无条件 UPDATE,可能覆盖同时发生的管理员重置。 - (c) logout 调用的 `ClearSession` 按编号无条件清空,会误清同时用密码登录的新设备的令牌。 - **证据** - 登录:`broker/authn.go:112-145`、`195-238`(见下方代码)。 - logout 的清空:`authn.go:240-260`。 - 改密:`identity/self.go:134-188`。 - 重置:`admin/endpoints_db.go:381-395` 之后才踢线;踢线只踢当时的当前连接:`broker/session.go:316-336`。 ```223:229:e:\code\NixMsg\internal\broker\authn.go writeErr := l.DB.Queue.Do(ctx, func(tx *sql.Tx) error { _, e := tx.Exec(` UPDATE endpoints SET session_hash = ?, session_issued_at = ?, session_used_at = ? WHERE id = ?`, hashHex, nowMs, nowMs, endpointID) return e }) ``` - **文档依据**:PRD F02(152)、F01(136-137);DEVELOPMENT 7.1(618)「所有变更都用带条件的 UPDATE」。 - **为何不是故意设计**:没有相关偏差记录,属于先检查后使用(TOCTOU)的竞态。 - **解决方案** 1. 登录写令牌改为 `WHERE id=? AND enabled=1 AND login_hash=?`,影响 0 行时返回认证失败,不计锁定。 2. 改密同样加 `login_hash` 和 `enabled` 条件,影响 0 行时返回 unauthorized。 3. connState 记下本连接令牌的哈希,logout 改为 `WHERE id=? AND session_hash=?`。 4. `handleHello` 在标在线前复读 `enabled` 和 `session_hash`,不匹配就断开。后台操作是「先提交再踢当前连接」,踢线之后才成为当前连接的会话,其 hello 一定晚于提交,能被这一步拦住。 - **改动文件**:`broker/authn.go`、`broker/session.go`、`broker/broker.go`、`identity/self.go`。 - **与其他模块的交互/冲突风险**:hello 多一次读库,走读池,WAL 模式下不阻塞写;和 I-4 同在 `handleHello`,需要合并改。 - **需补测试** - 注入一个在 Verify 里阻塞的 HashPool,登录进行中执行重置、停用、删除,放行后登录被拒,`session_hash` 仍为空。 - 改密版本同理,断言 `login_hash` 是管理员设的值。 - logout 与新设备同时登录后,新令牌仍然有效。 - **置信度**:代码阅读确定。 --- <sub>复审基线:main `4059a15`(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。</sub>
nixevol added the P1-highlane/brokerlane/identityreview-2026-09-30 labels 2026-09-30 13:56:53 +08:00
Author
Owner

已合入 origin/main 0c9b459。落地提交 91e887b fix: 完成 broker 复审 B-03 至 B-12 (#17)。

已合入 origin/main `0c9b459`。落地提交 `91e887b` fix: 完成 broker 复审 B-03 至 B-12 (#17)。
Sign in to join this conversation.