如何防止跨站WebSocket劫持?SaaS场景下含curl请求防护需求
如何防止非官方站点/工具伪造Origin并使用合法令牌连接WebSocket
针对你的场景(仅允许官方Web应用通过浏览器连接WebSocket,阻止curl等工具或第三方站点用用户令牌连接),可以通过以下几种务实的方案组合实现:
1. 拆分认证凭证,结合Cookie与前端临时令牌
把用户的认证逻辑拆分为两部分:
- 后端生成一个HttpOnly、SameSite=Strict的Cookie,存储用户的核心会话标识(无法被前端JS读取,避免XSS泄露)。
- 用户加载官方Web应用的WebSocket页面时,后端返回一个短期、绑定当前会话与Origin的临时令牌(存在前端内存中,不持久化)。
- WebSocket握手时,后端同时校验:
- 请求的Origin是否为官方域名;
- Cookie中的会话标识有效;
- 临时令牌与会话标识匹配且未过期。
这种方式下,curl用户需要同时获取到有效Cookie和临时令牌才能发起连接,而临时令牌随页面会话过期且无法通过JS窃取,大幅提高了伪造难度。
2. 使用会话绑定的WebSocket专属密钥
- 用户在官方应用登录后,每次进入需要WebSocket的页面时,向后端请求一个WebSocket专属的短期密钥,后端将该密钥与用户会话、允许的Origin绑定并存储(有效期设为10~30分钟)。
- 前端发起WebSocket连接时,在查询参数或自定义头中携带该密钥(比如
ws://your-domain/ws?key=xxx)。 - 后端校验时,需确认:
- 密钥属于当前用户会话;
- 请求的Origin与绑定的Origin一致;
- 密钥未过期;
- 访问令牌有效。
即使用户拿到了自己的访问令牌,没有这个绑定会话的短期密钥也无法建立连接,且密钥过期后需要重新从官方页面获取。
3. 校验浏览器专属请求头(辅助拦截)
浏览器发起WebSocket握手时会自动携带一些标准头,可作为辅助校验条件,拦截明显的非浏览器请求:
- 必须存在
Sec-WebSocket-Key和Sec-WebSocket-Version头,且格式符合规范; - 必须包含
Connection: Upgrade和Upgrade: websocket头; - 可校验
User-Agent是否为常见浏览器标识(虽可伪造,但能拦截大部分简单curl请求)。
注意:这个方法不能作为唯一校验,只能配合其他方案降低攻击面。
4. 在令牌中嵌入允许的Origin
颁发用户访问令牌时,将官方应用的Origin嵌入令牌的声明(比如JWT的allowed_origin字段)。WebSocket握手时:
- 首先校验令牌的有效性;
- 然后比对请求的Origin与令牌中嵌入的
allowed_origin是否完全一致。
这种方式直接从令牌层面限制了可使用的Origin,即使curl伪造了Origin,只要和令牌中的值不匹配就会被拒绝。但要注意,如果官方应用有多个域名,需要在令牌中嵌入所有允许的Origin;另外,用户更换浏览器或域名时需要重新获取令牌,可能影响体验。
关键说明
没有100%的方法能完全阻止技术熟练的用户用自己的令牌发起连接(毕竟用户自己的令牌本来就属于他),但通过上述方案的组合,可以大幅提高伪造门槛,满足SaaS应用的安全需求——核心思路是将WebSocket连接与官方Web应用的浏览器会话强绑定,让非浏览器环境难以模拟完整的认证流程。
内容的提问来源于stack exchange,提问作者user12865967
相关产品推荐
相关产品推荐

