WebSocket 协议本身不受浏览器同源策略(Same-Origin Policy)的直接限制,这意味着运行在 https://app.example.com 页面中的 JavaScript 可以合法地创建指向 wss://api.other-domain.com 的 WebSocket 连接。然而,浏览器在发起 WebSocket 握手请求时,会自动在 HTTP 头中添加 Origin 字段(如 Origin: https://app.example.com),表明该请求的来源。服务端需要显式检查这个 Origin 字段,决定是否允许该跨域连接。如果服务端未校验或错误配置了 Origin 白名单,可能导致跨站 WebSocket 劫持(CSWSH)攻击。
HTTP 的跨域资源共享(CORS)机制依赖服务端在响应头中设置 Access-Control-Allow-Origin 等字段来告知浏览器是否允许跨域请求。WebSocket 的跨域处理则发生在 HTTP 握手阶段:服务端在决定是否返回 101 Switching Protocols 响应时,需要检查请求头中的 Origin 字段,如果不在白名单中,应直接拒绝握手(返回 403 Forbidden)。值得注意的是,给 WebSocket 服务添加 Access-Control-Allow-Origin 响应头是无效的,因为握手成功后,通信就不再使用 HTTP 语义,CORS 机制也不适用。
在 Node.js 中使用 ws 库时,可以通过 verifyClient 回调函数来验证 Origin:检查请求头中的 origin 字段是否在内置的白名单列表中(如 ['https://app.example.com', 'http://localhost:3000']),如果不在列表中,返回 false 拒绝连接。在使用 Nginx 作为反向代理时,需要确保正确透传 Upgrade 和 Connection 头(proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";),否则 WebSocket 握手请求无法到达后端应用,导致跨域连接失败。在生产环境中,应该精确匹配可信域名,避免使用通配符(如 *)放行所有 Origin。