直接返回 true 的 CheckOrigin 是生产环境高危操作,会暴露跨站 WebSocket 劫持(CSWSH)风险;必须用可维护、可审计的白名单机制替代,默认 checkSameOrigin 因严格匹配协议+域名+端口而拒绝 localhost 开发请求。

直接返回 true 的 CheckOrigin 是生产环境的高危操作,等同于关闭 Origin 校验,会暴露在跨站 WebSocket 劫持(CSWSH)风险下。必须用可维护、可审计的白名单机制替代。
为什么默认 CheckOrigin 会拒绝 localhost 开发请求
gorilla/websocket 的默认 checkSameOrigin 函数要求 Origin 头必须与 r.Host 完全一致(协议 + 域名 + 端口),且不接受空或 null 值。这导致以下常见失败场景:
- 前端通过
file://双击打开 HTML,Origin为null,被直接拒绝 - Vite/HMR 代理到
http://localhost:8080,但页面实际来自http://localhost:3000,Origin 不匹配 - HTTPS 前端访问 HTTP 后端(或反之),协议不一致即失败
这不是 bug,而是安全默认值——它防止恶意网站直接 new WebSocket('ws://your-api.com/ws') 接入你的服务。
如何写一个兼顾开发与生产的 Origin 白名单函数
核心原则:允许明确列出的协议+域名组合,拒绝一切未声明来源;同时对本地开发做最小豁免(仅限非生产环境)。
- 用
url.Parse()解析Origin,提取u.Scheme和u.Host,避免字符串拼接误判 - 白名单条目应为完整 URL 字符串(如
"https://app.example.com"),而非仅域名,防止协议降级攻击 - 开发时可加宽松条件:
os.Getenv("ENV") == "dev"且u.Host以localhost:或127.0.0.1:开头才放行 - 永远不要用
strings.Contains(origin, "example.com")这类模糊匹配——evil-example.com也会通过
示例片段:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
func customCheckOrigin(r *http.Request) bool {
origin := r.Header.Get("Origin")
if origin == "" {
return false // 拒绝无 Origin 的请求(非浏览器客户端需另行处理)
}
u, err := url.Parse(origin)
if err != nil {
return false
}
allowed := []string{
"https://app.example.com",
"https://admin.example.com",
"http://localhost:3000",
"http://127.0.0.1:3000",
}
for _, a := range allowed {
au, _ := url.Parse(a)
if u.Scheme == au.Scheme && u.Host == au.Host {
return true
}
}
return false
}
CheckOrigin 里不能做的事
这个函数运行在 HTTP 请求解析阶段,处于连接升级前最轻量的上下文,因此有严格限制:
- 不能发起外部 HTTP 请求(如调用鉴权服务),会拖慢握手并引入单点故障
- 不能读取数据库或 Redis,除非你已做本地缓存且保证毫秒级响应
- 不能依赖 session 或 cookie——此时
http.Request尚未经过中间件,r.Cookies()可能为空或不可信 - 不能在其中解析 JWT 或验证 token,认证必须放在
Upgrade()调用之前单独完成
Origin 校验只管“谁在连”,不管“谁在连”。身份认证和权限检查是另一层事,别混进 CheckOrigin。
上线前必须确认的三件事
白名单配置容易漏掉细节,上线前务必逐项核对:
- 生产环境的
ENV变量是否设为"prod"?确保开发豁免逻辑不会生效 - 所有白名单条目是否都带协议?
"example.com"会被url.Parse解析失败,返回false - 前端实际部署域名是否 100% 匹配白名单?比如用了 CDN 后
Origin可能是https://cdn.example.com,而非源站域名
最常被忽略的一点:WebSocket 的 Origin 是浏览器自动注入的,前端 JS 无法修改。所以白名单必须完全贴合前端真实部署地址,而不是“我以为它该是什么”。










