You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Node.js实现WebSocket请求来源域名的100%防伪造验证?

解决方案:基于域名控制权验证的WebSocket来源校验

要实现100%可靠的域名来源验证,核心是让客户端证明自己实际控制目标域名,而非依赖可伪造的请求头。以下是适配你的Node.js场景的方案,全程耗时可控制在1秒内:

核心流程(分两步)

1. 预验证:域名控制权证明

客户端发起WebSocket连接前,先完成域名归属校验:

  • 客户端向服务器发HTTP POST请求,携带自身id(或直接传域名)。
  • 服务器通过id从数据库查出对应域名targetDomain,生成10秒有效期的一次性随机挑战字符串(用crypto.randomBytes(32).toString('hex')生成),将挑战、域名、过期时间存入缓存(Redis/内存缓存均可,保证查询速度)。
  • 服务器返回挑战给客户端,要求客户端在https://${targetDomain}/.well-known/ws-challenge/${challengeId}路径下返回该挑战内容(客户端需提前部署静态文件服务,或动态生成该路径的响应)。
  • 客户端告知服务器准备就绪后,服务器立即发起GET请求到上述客户端域名路径,验证返回内容与缓存的挑战是否一致。
  • 验证通过后,服务器生成绑定该域名的临时JWT令牌(有效期设为30秒),令牌payload包含domain和exp字段,返回给客户端。

2. WebSocket连接校验

客户端发起WebSocket连接时,在请求头Authorization字段携带临时令牌:

  • 服务器在WebSocket握手的upgrade事件中解析令牌,验证签名、有效期,以及令牌中的domain是否与数据库中id对应的域名匹配(若客户端传了id)。
  • 验证通过则建立连接,否则直接拒绝。

Node.js代码示例

依赖安装

npm install express ws jsonwebtoken crypto redis axios

核心代码片段

预验证服务(Express)

const express = require('express');
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const redis = require('redis');
const axios = require('axios');

const app = express();
app.use(express.json());

const redisClient = redis.createClient();
redisClient.connect();

// 模拟从数据库获取域名的逻辑(替换为你的实际查询代码)
async function getDomainById(id) {
  const domainMap = { 'client_001': 'demo-client-1.com', 'client_002': 'demo-client-2.com' };
  return domainMap[id];
}

// 生成并存储挑战
app.post('/ws/pre-verify', async (req, res) => {
  const { id } = req.body;
  const targetDomain = await getDomainById(id);
  if (!targetDomain) return res.status(404).send('无效ID');

  const challengeId = crypto.randomBytes(16).toString('hex');
  const challengeValue = crypto.randomBytes(32).toString('hex');
  // 缓存挑战,10秒后自动过期
  await redisClient.setEx(challengeId, 10, JSON.stringify({ domain: targetDomain, value: challengeValue }));

  res.json({
    challengeId,
    challengeValue,
    verifyPath: `/.well-known/ws-challenge/${challengeId}`
  });
});

// 验证挑战并返回临时令牌
app.post('/ws/verify-challenge', async (req, res) => {
  const { challengeId } = req.body;
  const challengeData = await redisClient.get(challengeId);
  if (!challengeData) return res.status(400).send('挑战已过期或无效');

  const { domain, value } = JSON.parse(challengeData);
  try {
    // 请求客户端域名验证挑战,超时设为500ms
    const response = await axios.get(`https://${domain}/.well-known/ws-challenge/${challengeId}`, { timeout: 500 });
    if (response.data.trim() !== value) return res.status(403).send('挑战内容不匹配');

    // 生成30秒有效期的临时令牌
    const token = jwt.sign(
      { domain, exp: Math.floor(Date.now() / 1000) + 30 },
      'your-secure-jwt-secret' // 替换为你的密钥
    );
    res.json({ token });
  } catch (err) {
    res.status(403).send('域名控制权验证失败');
  }
});

WebSocket服务(ws库)

const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const { createServer } = require('http');

const server = createServer(app);
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws, req) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    ws.close(401, '未授权');
    return;
  }

  const token = authHeader.split(' ')[1];
  try {
    const payload = jwt.verify(token, 'your-secure-jwt-secret');
    // 可额外验证payload中的domain是否匹配客户端传入的id(若需要)
    console.log(`已验证连接来自域名:${payload.domain}`);
    
    // 后续业务逻辑
    ws.on('message', (data) => {
      ws.send(`收到消息:${data}`);
    });
  } catch (err) {
    ws.close(403, '无效令牌');
  }
});

server.listen(3000, () => {
  console.log('服务运行在端口3000');
});

方案可靠性说明

  • 完全避免请求头伪造:攻击者必须实际控制目标域名,才能返回正确的挑战内容,从根源上解决伪造问题。
  • 耗时可控:预验证的HTTP请求超时设为500ms,加上令牌生成和校验,总耗时远低于1秒。
  • 风险可控:临时令牌有效期极短,即使泄露也无法被长期滥用。

优化建议

  • 缓存优先用Redis或内存缓存(如node-cache),比数据库查询更快,符合耗时要求。
  • 给客户端的验证路径添加IP白名单,只允许你的服务器IP访问,避免他人恶意请求。
  • 若客户端无法部署静态文件,可改用DNS TXT记录验证(服务器查询客户端域名的TXT记录是否包含挑战值),但需调整DNS查询超时时间(建议设为800ms内)。

内容的提问来源于stack exchange,提问作者Gura

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:27:43