贝利信息

如何安全地在 Socket.IO 中传递用户 ID 进行数据库操作

日期:2026-01-02 00:00 / 作者:聖光之護

本文详解为何直接从 dom 读取用户 id 并通过 socket.io 发送给服务器存在严重安全风险,并提供基于 jwt 和服务端身份绑定的可靠替代方案。

在您当前的实现中,前端通过

{{data}} 暴露用户 ID,再用 document.getElementById('iduser').innerHTML 获取该值,最后通过 Socket.IO 发送给服务器执行 SQL 更新(如 UPDATE users SET money = money + ? WHERE id = ?)。这种做法极不安全,绝对不可用于生产环境。

❌ 为什么这是危险的?

✅ 安全实践:服务端绑定 + 无状态验证

1. 在 Socket.IO 连接时完成用户身份绑定

利用 Express 的 Session 或 JWT,在建立 Socket 连接时验证并关联用户身份:

// app.js —— Socket.IO 初始化阶段
io.use((socket, next) => {
  const token = socket.handshake.auth.token; // 或从 cookie/headers 提取
  if (!token) return next(new Error('Authentication error'));

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    socket.user = decoded; // 将用户信息挂载到 socket 实例
    next();
  } catch (err) {
    next(new Error('Invalid token'));
  }
});

io.on('connection', (socket) => {
  console.log('User connected:', socket.user.id); // ✅ 此处 user.id 来自可信 JWT,不可伪造

  // 监听充值事件(无需客户端传 userId!)
  socket.on('addMoney', (payload) => {
    const { amount } = payload;
    const userId = socket.user.id; // ✅ 唯一可信来源

    db.query(
      'UPDATE users SET money = money + ? WHERE id = ?',
      [amount, userId],
      (err, result) => {
        if (err) throw err;
        socket.emit('moneyUpdated', { newBalance: result.affectedRows });
      }
    );
  });
});

2. 前端调用时无需暴露或传输 userId

// client.js —— 安全调用示例
const token = document.querySelector('meta[name="jwt-token"]')?.content;
const socket = io({
  auth: { token } // 将 token 传入握手阶段
});

document.getElementById('add100Btn').addEventListener('click', () => {
  socket.emit('addMoney', { amount: 100 }); // ✅ 不传 userId!
});
? 提示:可在 HTML 中注入 JWT(需设置 httpOnly: false 且仅限短期有效 Token),或通过安全 Cookie + 后端中间件自动注入。

3. 额外加固建议