直接放行CheckOrigin返回true最危险——它完全放弃Origin校验,使跨站WebSocket劫持风险拉满;浏览器自动注入Origin值(如http://localhost:5173),与后端Host不一致即403;白名单须精确匹配协议、域名、端口,禁用通配符;空Origin或非浏览器客户端需结合Token/IP白名单处理。
直接在 checkorigin 里写 return true 是最省事的解法,也是最危险的——它等于把 websocket 握手门禁换成敞开的旋转门,任何来源都能带着 cookie 直接连进来,跨站 websocket 劫持风险拉满。
为什么浏览器发起的 WebSocket 总过不了 CheckOrigin
不是你没发 Origin,是 JavaScript 根本不让你控制它。调用 new WebSocket('ws://localhost:8080') 时,浏览器自动填入 Origin 头,值就是当前页面的 document.origin(比如 http://localhost:5173 或 file://)。本地开发时常见三种失败场景:
- 前端双击 HTML 打开 →
Origin是"null"或空字符串 - Vite/HMR 服务跑在
http://localhost:5173,后端在:8080→ 协议+域名+端口三者不完全匹配,CheckOrigin默认逻辑直接拒掉 - 前端部署在 Nginx 反向代理后,但没透传
Origin或改写了Host→ 两者比对失败
如何写一个安全又可用的 CheckOrigin 函数
核心原则:白名单必须精确到协议、域名、端口;开发环境可放宽,但不能放任空 Origin 或通配符;非浏览器客户端(如 curl、嵌入式设备)不带 Origin,需单独策略处理。
- 生产环境只列具体条目:
"https://admin.example.com"、"https://client.example.com:8443",不加尾部斜杠,不写*.example.com - 开发环境允许
"http://localhost:3000"、"http://127.0.0.1:5173",但禁止"http://localhost:*"—— 端口通配会绕过校验 - 显式判断空或
"null":origin == "" || origin == "null",再决定是否豁免(仅限可信本地调试) - 非浏览器客户端建议走另一条路径:要么在 Upgrade 前用 Token 认证(如从 query string 提取
token=xxx),要么结合 IP 白名单 + 速率限制,而不是靠 Origin 放行
CheckOrigin 和认证逻辑的执行顺序很关键
CheckOrigin 只管握手阶段的来源合法性,它不验证用户身份。真正的认证(JWT、Session、OAuth)必须在 Upgrader.Upgrade() 调用之前完成,否则连接一旦建立,就再也无法拦截恶意凭证。
- 错误做法:先
Upgrade,再读消息、解析 token、发现非法就关连接 —— 此时攻击者已完成握手,可能已触发业务逻辑 - 正确做法:从
r.URL.Query().Get("token")或r.Header.Get("Authorization")提前取凭据,校验失败直接http.Error()返回 401,根本不会走到Upgrade - 注意:如果用了反向代理(如 Nginx),确保它透传了原始请求头或 query 参数,否则认证字段可能丢失
Origin 校验不是“要不要做”的问题,而是“怎么精准做”的问题。漏掉端口、误信通配符、混淆校验与认证时机,这三个点最容易被忽略,也最常导致线上漏洞。


















