从 #8 拆出来的最后一小块。#8 的两条主任务(bcrypt 哈希 + 密码不进 URL)都已经做完关掉了,服务端现在是干净的;剩下的这一条在客户端。
现状
public/game.js:17957:
localStorage.setItem('pvp_user', JSON.stringify({ username: name, password: pass }));
登录成功后,明文密码被写进了浏览器的 localStorage,而且会一直留在那里。
为什么还值得改
服务端已经不存明文了(bcrypt),但客户端这一份是明文的:
- localStorage 里的东西,任何在这个域名下跑起来的脚本都能读到 —— 一旦哪天再冒出一个 XSS,攻击者拿到的就不是"能在你浏览器里搞事",而是直接拿走密码本身
- 而很多人到处用同一个密码,泄露的影响会跑到这个游戏之外去
- 共用电脑上,下一个人打开开发者工具就能看见
怎么改
标准做法是把"记住我"从存密码改成存 token:
- 登录成功时,服务端生成一个随机字符串当 token(
crypto.randomBytes(32).toString('hex')),存在 users.json 里那个用户名下
- 返回给客户端,客户端
localStorage.setItem('pvp_user', JSON.stringify({ username, token })) —— 不存密码
- 之后所有需要身份的请求(
/shop/buy、/shop/inventory)带 token,不带密码
authedUser() 里加一条:token 匹配也算通过
好处是顺带解决了另一件事 —— token 可以作废(改密码就换一个新 token,所有老设备自动掉线),密码做不到这个。
一个更省事的中间方案:先什么都不改,只是不再把密码写进 localStorage,每次打开都重新登录一次。安全性立刻到位,代价是要重新输密码。想省事可以先走这个,token 以后再说。
English: Follow-up split out of #8. The server side is done (bcrypt at rest, no passwords in URLs), but public/game.js:17957 still writes the plaintext password into localStorage and there is no token mechanism. Any future XSS would hand over the password itself rather than just a session. Suggested fix: issue a random token on login, store {username, token} client-side, and accept the token in authedUser() — which also gives you revocation, something a stored password can never do. A cheaper interim step: just stop persisting the password and make people log in each time.
从 #8 拆出来的最后一小块。#8 的两条主任务(bcrypt 哈希 + 密码不进 URL)都已经做完关掉了,服务端现在是干净的;剩下的这一条在客户端。
现状
public/game.js:17957:登录成功后,明文密码被写进了浏览器的 localStorage,而且会一直留在那里。
为什么还值得改
服务端已经不存明文了(bcrypt),但客户端这一份是明文的:
怎么改
标准做法是把"记住我"从存密码改成存 token:
crypto.randomBytes(32).toString('hex')),存在users.json里那个用户名下localStorage.setItem('pvp_user', JSON.stringify({ username, token }))—— 不存密码/shop/buy、/shop/inventory)带 token,不带密码authedUser()里加一条:token 匹配也算通过好处是顺带解决了另一件事 —— token 可以作废(改密码就换一个新 token,所有老设备自动掉线),密码做不到这个。
一个更省事的中间方案:先什么都不改,只是不再把密码写进 localStorage,每次打开都重新登录一次。安全性立刻到位,代价是要重新输密码。想省事可以先走这个,token 以后再说。
English: Follow-up split out of #8. The server side is done (bcrypt at rest, no passwords in URLs), but
public/game.js:17957still writes the plaintext password intolocalStorageand there is no token mechanism. Any future XSS would hand over the password itself rather than just a session. Suggested fix: issue a random token on login, store{username, token}client-side, and accept the token inauthedUser()— which also gives you revocation, something a stored password can never do. A cheaper interim step: just stop persisting the password and make people log in each time.