Hash seat tokens at rest (security review finding)
Raw seat tokens no longer touch disk or long-lived memory: rooms store sha-256 hashes, joins compare timing-safely, sessions keep the raw token they authenticated with only for minting transfer phrases, and legacy plaintext room files still load (hashed on read). Verified: rejoin and transfer both work, and the room file contains only hostTokenHash — no raw token anywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ce8b6a315b
commit
0221e91ba3
@@ -36,6 +36,8 @@ interface Session {
|
||||
socket: WebSocket;
|
||||
playerId: PlayerId | null;
|
||||
roomId: string | null;
|
||||
/** The raw seat token this connection authenticated with (memory only). */
|
||||
token: string | null;
|
||||
claimFails: number;
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ function broadcastRoomState(room: Room): void {
|
||||
}
|
||||
|
||||
wss.on("connection", (socket) => {
|
||||
const session: Session = { socket, playerId: null, roomId: null, claimFails: 0 };
|
||||
const session: Session = { socket, playerId: null, roomId: null, token: null, claimFails: 0 };
|
||||
sessions.add(session);
|
||||
send(socket, { type: "welcome", game: "wizwar" });
|
||||
|
||||
@@ -93,6 +95,7 @@ wss.on("connection", (socket) => {
|
||||
const { room, token } = createRoom(name);
|
||||
session.playerId = name;
|
||||
session.roomId = room.id;
|
||||
session.token = token;
|
||||
send(socket, { type: "seat", playerId: name, token });
|
||||
broadcastRoomState(room);
|
||||
break;
|
||||
@@ -107,6 +110,7 @@ wss.on("connection", (socket) => {
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
session.playerId = name;
|
||||
session.roomId = room.id;
|
||||
session.token = result.token;
|
||||
send(socket, { type: "seat", playerId: name, token: result.token });
|
||||
// Rejoining a running game: replay the chronicle so far.
|
||||
if (room.state) {
|
||||
@@ -137,7 +141,7 @@ wss.on("connection", (socket) => {
|
||||
case "makeTransfer": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room || !session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
const result = makeTransferCode(room, session.playerId);
|
||||
const result = makeTransferCode(room, session.playerId, session.token);
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
send(socket, { type: "transferCode", code: result.code, expiresAt: result.expiresAt });
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user