WebSocket是通过HTTP协议发起的一个双向全双工的通信协议。WebSocket协议被广泛用在现代WEB程序中用于数据流的传输和异步通信。
大多数的Web浏览器和Web网站都是使用HTTP协议进行通信的。通过HTTP协议,客户端发送一个HTTP请求,然后服务器返回一个响应。通常来说,服务端返回一个响应后,这个HTTP请求事务就已经完成了。即使这个HTTP连接处于keep-alive的状态,它们之间的每一个工作(事务)依然是请求与响应,请求来了,响应回去了。这个事务就结束了。所以通常来说,HTTP协议是一个基于事务性的通信协议。
而WebSocket呢,它通常是由HTTP请求发起建立的,建立连接后,会始终保持连接状态。客户端和服务端可以随时随地的通过一个WebSocket互发消息,没有所谓事务性的特点。这里要注意了,源于其双向全双工的通信特点,在一个WebSocket连接中,服务端是可以主动发送消息的哦,这一点已经完全区别于HTTP协议了。
因此,基于以上特点,WebSocket通常用于低延迟和允许服务器发送消息的场景。例如,金融行业常用WebSocket来传输实时更新的数据。
wss协议通过TLS连接建立一个WebSocket;ws协议是不经过加密的连接。
通过HTTP协议,客户端和服务端进行一个WebSocket握手。
客户端的握手请求:
如果服务端接受这个连接,它会返回一个WebSocket回复:
之后,这个WebSocket的网络连接会保持开启状态,任意一方都可以直接发送WebSocket信息。
关于WebSocket握手时的一些特性:
Connection和Upgrade头部用来标识这是一个WebSocket握手消息。Sec-WebSocket-Version请求头明确了一个客户端希望使用的WebSocket协议版本。版本13最常用。Sec-WebSocket-Key请求头包含了一个base64编码的随机值,在每个WebSocket握手请求中,它一定是随机生成的。Sec-WebSocket-Accept响应头的值是客户端发送的握手请求中Sec-WebSocket-key的哈希值,并与协议规范中定义的特定字符串连接。这样做的目的是匹配每一对握手请求,防止由于错误的配置或者缓存代理导致的连接错误。
一旦一个WebSocket建立成功后,客户端和服务端任意一方都可以立即异步的发送消息。
例如,客户端可以通过JavaScirpt来简单的发送一个的消息:
本质上,WebSocket消息体可以包含任意数据格式的内容。在现代应用程序中,通过WebSocket发送JSON格式的消息体比较常见。
例如,一个聊天机器人的WEB程序可以使用WebSocket发送如下的内容:
寻找WebSockets的安全漏洞最常用的方法就是,操纵WebSocket的通信流,通过非预期的输入来进行安全测试。本章节主要讨论,如何操纵WebSocket的通信流,以及和操纵普通的HTTP通信相比,操控WebSocket通信时要注意哪些问题。
拦截和修改WebSocket信息
重放和生成一个新的WebSocket消息
操控WebSocket连接
和HTTP报文的测试流程一样,你可以使用Burp Proxy来拦截和修改WebSocket消息。


注意:你可以在Proxy -> Options -> Intercept WebSockets Messages 中配置只拦截客户端 -> 服务端, 或者服务端 -> 客户端方向的WebSocket消息。(默认情况下两个方向都拦截)
除了拦截和修改一个WebSocket消息之外,你可能也需要重放或者新生成一个WebSocket消息。你可以通过Burp Repeater来实现这些(注意:WebSocket的Repeater面板UI界面不一样,增加了很多小功能,比如管理WebSocket连接、已经重放的消息历史,师傅们可以自己体验一下!):

当然,除了操控WebSocket双向中传递的消息体之外,我们也可以操纵WebSocket的握手环节。这可以引入一些有趣的高级的攻击手法。例如:WebSocket跨域劫持,这会在第四章节中讲解。
有很多场景下,操控WebSocket握手连接是非常有必要的:
它可以扩展你的攻击面
一些攻击手法可能会导致你的连接断开,所以你需要去重新建立一个新的连接
Token或者其它的数据在原始的握手请求中可能被窃取或者需要更新
你可以通过刚才讲到的Burp Repeater操控WebSocket握手:

实际上,几乎所有的Web漏洞都有可能出现在WebSocket中。因为,WebSocket本质上就是一个通过HTTP建立连接的双向全双工的通信协议而已,但由于其相比HTTP多了一“工”的特性,可能会出现一些新的攻击场景。
常见的攻击场景如下:
用户的输入被服务器以不安全的方式进行处理,导致了例如SQL注入、XXE等注入攻击。
一些盲注漏洞可能会通过WebSocket引起,可以利用带外技术进行探测。
如果攻击者控制的数据通过WebSocket传输到了其它用户的客户端处,这可能会引起XSS或者其它客户端类型的漏洞。
通过篡改 WebSocket 消息的内容,可以发现并利用大多数基于输入的漏洞。
例如,一个具有聊天功能的Web程序使用WebSocket在客户端和服务端之间传输消息。当一个用户输入聊天消息时,如下的一个WebSocket消息被发送到服务端:
服务端会将这个消息内容通过WebSocket转发给另外一个用户Tom,然后在Tom的浏览器中被JS渲染为一段HTML代码。
当服务器没有对转发的内容做安全防御或过滤时,可能就会引发XSS攻击。
许多WebSockets漏洞只能在握手环节被发现和利用。这些漏洞往往涉及设计缺陷,例如:
无条件信任HTTP头,导致某些安全策略可以被绕过。例如:X-Forwarded-For头。
会话处理机制存在缺陷,因为处理WebSocket消息的会话上下文通常由握手消息的会话上下文确定。
应用程序自定义的HTTP标头引入的攻击面。
一个操纵WebSocket握手流程的案例是,当一个在线聊天程序通过WebSocket传输消息时,会过滤WebSocket消息中的XSS载荷,并且封禁攻击者的IP,使该IP短时间内无法再次发起WebSocket连接。此时,可以使用X-Forwarded-For头来绕过黑名单,发起WebSocket握手请求建立连接。
然后,可以多次绕过黑名单限制,进行XSS Bypass。
有一种WebSocket攻击类似于JSONP劫持,属于CSRF攻击的一种,它被称为cross-site WebSocket hijacking 攻击。
跨站WebSocket劫持(也称为跨域WebSocket劫持)是一种由于WebSocket握手流程的安全缺陷所导致的跨站点请求伪造(CSRF)漏洞。
当WebSocket握手请求仅依靠HTTP cookie进行会话处理并且不包含任何CSRF token或其他不可预测的值时,就会出现这种漏洞。
攻击者可以在自己的站点上创建一个恶意网页,从而建立与易受攻击的应用程序的跨站点WebSocket连接。 该应用程序将在受害用户与该应用程序的会话的上下文中处理连接。
然后,攻击者的页面可以通过WebSocket连接向服务器发送任意消息,并读取从服务器接收回的消息内容。 这意味着,与常规的CSRF不同,攻击者可以与受攻击的应用程序进行双向交互。
一个成功的跨站WebSocket劫持攻击通常会使攻击者能够:
伪装成受害者用户来执行未经授权的行为,与常规CSRF一样,攻击者可以将任意消息发送到服务器端应用程序。 如果应用程序使用客户端生成的WebSocket消息执行任何敏感操作,则攻击者可以跨域生成合适的消息并触发这些操作。例如绑定手机号、修改密码等。
访问受害者的敏感数据。 与常规CSRF不同,跨站点WebSocket劫持使攻击者可以通过被劫持的WebSocket与易受攻击的应用程序进行双向交互。 如果应用程序使用服务器生成的WebSocket消息将任何敏感数据返回给用户,则攻击者可以拦截这些消息并捕获受害用户的数据。这意味着无需跨域方法的支持(JSONP、CORS),也可以读取受害者的数据。
由于跨站点WebSocket劫持攻击本质上是WebSocket握手上的CSRF漏洞,因此执行攻击的第一步是检查应用程序执行的WebSocket握手过程是否针对CSRF进行了保护。
就正常的CSRF攻击流程而言,通常需要找到一个握手消息,该消息仅依赖HTTP cookie进行会话处理,并且在请求参数中不使用任何token或其他不可预测的值。
例如,以下WebSocket握手请求可能容易受到CSRF的攻击,因为唯一的session token是在cookie中传输的:
注意:
Sec-WebSocket-Key请求头包含的随机值主要用于防止代理服务器缓存错误,并且不用于身份验证或会话处理目的。
如果WebSocket握手请求易受CSRF的攻击,则攻击者的网页可以执行跨站点请求,与易受攻击的服务器后端接口建立WebSocket。 攻击中接下来发生的事情完全取决于应用程序的逻辑以及它如何使用WebSockets。 攻击可能涉及:
例如,一个跨站WebSocket劫持读取用户聊天记录的POC如下,用户在建立WebSocket连接后,向服务器发送'READY'字符串,即可接收到服务器传来的历史聊天记录。
wss://协议,(基于TLS的Websockets)