Repository navigation
fix(accounts,deploy): oauthBind 核对邮箱、占位 secret 改 0644、推送中继独立限流区 - #199
Merged
Merged
Conversation
Owlbay
force-pushed
the
fix/accounts-deploy-hardening
branch
from
November 2, 2026 21:29
70bd2f0 to
deb3896
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要 / Summary
中文:账号与部署的三处加固(审计 BA-58、BA-59、BA-60)。
go/internal/cloud/accounts/oauth.go):oauthBind在事务内锁账号并重读fresh,却不比较fresh.Email与提供方给的邮箱。事务外读到同邮箱账号后、绑定事务前邮箱被改走(改邮箱确认落在这个窗口)时,会跳过「未验证则清口令、撤设备」的分支,让受害者的第三方身份绑到攻击者仍持有口令的账号上。现在锁住后邮箱不一致答409 conflict(details.reason = "email_changed"),不绑定、不改账号。deploy/saas/init-secrets.sh):oauth_github_secret、oauth_google_secret、push_relay_apns_key、push_relay_fcm_credentials是要运维事后写入的占位文件,却走默认chmod 444,按手册echo … > secrets/…写入会 Permission denied。改为 0644(目录仍 0700),手册注明已有部署先chmod 644。deploy/saas/nginx.conf.template):push.<域>的/v1/register与app.站点登录共用auth限流区(30r/m),同出口 IP 的推送登记会挤掉登录额度;/v1/push在代理层不限流。现在push_register(30r/m,burst 20)和宽松的push_send(600r/m,burst 100)各用各的区。English: Three hardening fixes for accounts and deployment (audit BA-58, BA-59, BA-60).
oauth.go):oauthBindlocks the account and re-readsfreshinside the transaction but never comparesfresh.Emailwith the provider's email. If the email is changed between the out-of-transaction read and the binding transaction (an email-change confirmation landing in that window), the "unverified: clear the password and revoke devices" branch is skipped and the victim's third-party identity gets bound to an account whose password the attacker still holds. After the lock, a mismatching email now answers409 conflict(details.reason = "email_changed") with nothing bound or changed.init-secrets.sh): the operator-filled placeholdersoauth_github_secret,oauth_google_secret,push_relay_apns_keyandpush_relay_fcm_credentialsgot the defaultchmod 444, soecho … > secrets/…from the runbook failed with Permission denied. They are now 0644 (directory still 0700); the runbook tells existing deployments tochmod 644first.nginx.conf.template):/v1/registeronpush.<domain>shared the 30r/mauthlogin zone, so push registrations from one egress IP ate login allowance, and/v1/pushhad no proxy-level limit.push_register(30r/m, burst 20) and a loosepush_send(600r/m, burst 100) now have their own zones.关联 issue / Related issues
Closes #193
改动范围 / Scope
go/internal/cloud/accounts)deploy/saas/)契约与协议变化 / Contract and protocol changes
无 / None(
email_changed只是conflict的details.reason新取值,答复形状不变 /email_changedis only a newdetails.reasonvalue ofconflict; the shape is unchanged)数据库迁移 / Database migrations
无 / None
测试 / Testing
新增
TestOAuthBindRechecksEmail(go/internal/cloud/server/oauth_test.go):测试先锁住账号行,让绑定事务卡在LockAccount,改掉邮箱后放行;修复前答 200 并建出会话,修复后答 409 且无绑定、口令与设备不动。 / NewTestOAuthBindRechecksEmail: the test holds the account row lock so the binding transaction waits inLockAccount, changes the email, then releases; before the fix it answered 200 and created a session, after the fix 409 with nothing bound and password and devices untouched.init-secrets.sh在临时目录以非 root 实跑:四个占位文件 0644,echo … >写入成功(master_key仍 0444)。 /init-secrets.shwas run as non-root in a temp dir: the four placeholders are 0644 andecho … >succeeds (master_keystays 0444).渲染后的 nginx 配置用
nginx:1.29.3-alpine的nginx -t通过。 / The rendered nginx config passesnginx -tonnginx:1.29.3-alpine.单元测试 / Unit tests:
pnpm go:check(fmt、vet、staticcheck、go test -race、生成物,一次性 PostgreSQL / Redis 容器,已清理 / throwaway PostgreSQL and Redis containers, removed afterwards)集成测试 / Integration tests: 无 / None
跨仓端到端 / Cross-repo e2e: 无 / None
镜像 / Images: 无 / None
pnpm check与 / andpnpm repo:test检查清单 / Checklist
CHANGELOG.md的 Unreleased / Recorded under Unreleased inCHANGELOG.md(与 fix(relay): 边缘写队列加上限、慢读客户端被断开,空闲判定计入未收齐的帧,节点间写侧溢出立即断开 #196、fix(cloud): 落盘前把路由表里在线的主机记成此刻在线,最近在线与核心版本随落盘前进 #197 同在顶部的## Unreleased,合并后一方可能需要解冲突,保留各自的条目即可 / the## Unreleasedheading at the top is shared with fix(relay): 边缘写队列加上限、慢读客户端被断开,空闲判定计入未收齐的帧,节点间写侧溢出立即断开 #196 and fix(cloud): 落盘前把路由表里在线的主机记成此刻在线,最近在线与核心版本随落盘前进 #197; whoever merges last keeps every bullet)需要维护者提供的外部条件 / External prerequisites
已有部署:占位文件已是 0444 的,写入前先
chmod 644;代理配置改动需docker compose --profile proxy up -d --force-recreate proxy重新加载。 / Existing deployments: placeholders already 0444 needchmod 644before writing; reload the proxy withdocker compose --profile proxy up -d --force-recreate proxy.已知限制 / Known limitations
init-secrets.sh不覆盖已有文件,所以不会自动改已有部署里的权限。 /init-secrets.shnever overwrites existing files, so it does not change permissions on existing deployments.