https://www.cnblogs.com/chyingp/p/websocket-deep-in.html
https://cloud.tencent.com/developer/article/1887095
https://www.ruanyifeng.com/blog/2017/05/websocket.html
websocket是一种网络传输协议,可以在单个tcp连接上进行全双工通讯,位于osi模型的应用层(http也位于应用层)
在 WebSocket 握手成功之后,数据的传输是通过已经建立的 TCP 连接进行的,不再需要 HTTP 的传输头等额外的开销。
websocket与http关系
websocket复用了http的握手通道,在http连接的基础上,客户端发起协议升级,采用的是标准的http报文格式,且只支持get方法,【最后数据传输是在tcp协议上进行传输的,握手连接是http协议】
客户端
GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade // 表示协议升级
Upgrade: websocket // 表示升级到websocket协议
// websocket版本,如果服务器不支持此版本,则会返回Sec-WebSocket-Version 的header,里面有所有他支持的版本
Sec-WebSocket-Version: 13
// 与服务器的Sec-WebSocket-Accept配套使用,提供基本的防护,比如无意链接、恶意连接
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
服务器端
HTTP/1.1 101 Switching Protocols // 协议切换,完成协议升级
Connection:Upgrade
Upgrade: websocket
// 这个值与客户端Sec-WebSocket-Key有关,通过SHA1散列算法获取摘要信息值,并转成base64字符串
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=·
websocket的api
var ws = new WebSocket('ws://localhost:8080');
执行了上面的语句,websocket就会进行连接
websocket.readyState
返回实例对象的当前状态
- 0(connecting):正在连接
- 1(open):连接成功可以通信
- 2(closing):正在关闭
- 3(closed):连接已关闭,或打开链接失败
websocket.onOpen()
用于连接成功后的回调函数
websocket.onClose()
用于连接管壁厚的回调属性
websocket.onMessage()
收到服务器数据后的回调函数
websocket.send()
向服务器发送数据的函数
websocket.error()
用于报错时的回调函数
websocket.bufferedAmount
判断发送是否结束(表示还有多少二进制字节的数据没有发送出去)
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
websocket优点
- 基于tcp协议基础上的,服务器端实现起来比较容易
- 与http有良好的兼容性,能够通过http代理(握手阶段采用http协议,不容易被屏蔽)
- 数据格式比较轻量,开销小
- 没有同源限制,客户端可以与任意服务器通信
轮询(短轮询)、长轮询和websocket区别
轮询(短轮询):浏览器每隔一段时间向服务器发送一次请求,服务器返回最新的数据给浏览器
长轮询:客户端发器请求,服务器端在收到后,会不直接进行响应,将请求挂起,然后判断请求的数据是否有更新,或者超时才会返回给客户端。 eg:comet的实现方式之一,另一种(http://www.52im.net/thread-336-1-1.html%235)
websocket:全双工协议,服务器端游数据更新后可以直接发消息给客户端
代理缓存污染攻击(ws引入掩码计算为了防止此攻击)
- 攻击者发送ws协议升级到攻击者服务器
- 升级请求发送到了代理服务器,经攻击者服务器响应后,代理服务器将响应回复给攻击者,连接通道打通
- 攻击者通过WebSocket向攻击者服务器发送数据,其中包含被攻击资源的地址,以及一个伪造的host(指向被攻击资源的服务器)
- 代理服务器会缓存数据,当下一次被攻击者发送请求时,会直接将缓存数据发给被攻击者
只能说不能完全防范,但是加大了难度
websocket与cookie和token
websocket通过token传递信息,请求连接的时候一般不会传入cookie,但配置后可传(不管是连接还是数据传输时都是可以的)!!!
原因:
- 理论上websocket连接请求的时候是可以带上cookie的,因为他是在http的基础上建立的,可以使用http的特性,如在握手时使用cookie进行数据传输和身份认证,但是这个cookoe不受同源策略限制。【ps:在发送数据时,因为走的tcp协议了,不在需要http的传输头等额外开销,但是如果需要的话,可以配置!】
- websocket是否携带cookie是可配置的,一些安全性高的站点可能会禁止websocket携带cookie以增加安全性和隐私性
- 实际使用上并没有带上cookie,而是一般使用token进行验证,客户端请求时携带token,服务端收到请求,校验toekn时效性,判断客户端是否有操作权限,token可以避免CSRF攻击(token可以在客户端和服务器端进行交换,每次请求动态生成,攻击者无法预判token值)
websocket配置cookie可携带
- 在服务器端添加自定义头部信息,例如Set-Cookie,告诉客户端允许携带cookie,下方以node举例
// 服务器端
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', (socket, req) => {
// 允许携带 Cookie
socket.upgradeReq.headers.cookie = req.headers.cookie;
// 处理 WebSocket 连接
// ...
});
- 客户端设置相应头部信息,例如** Cookie**头,在浏览器中使用js,举例
const socket = new WebSocket('ws://localhost:8080', {
headers: {
'Cookie': document.cookie
}
});
为什么websocket不建议携带cookie
在 WebSocket 连接中携带 Cookie 会增加一定的安全风险,因此建议只在必要的情况下允许携带 Cookie,在实际过程中,按需配置
- 如果攻击者能够截获websocket连接并获取cookie,就可以冒充用户,获取用户敏感信息,或者进行恶意操作
- 用户隐私问题,websocket携带cookie,当用户进行一些操作,网站可能会记录这些操作并与用户的cookie连接起来,从而产生一些隐私风险
- 如果服务器用cookie来维持会话状态,那么websocket(携带了cookie的)可能会干扰这种状态的维护,导致系统异常
具体情况如下:
websocket是基于tcp协议通信,不同于http协议的“请求-响应”模式,一旦建立了连接,就可以客户端和服务器端双向通信,可以发送多条消息,这些消息时使用websocket自己的协议进行传输的,因此不会像http消息一样携带cookie。因此如果服务器是使用cookie来维持通信的话,当出现多个客户端连接时,没有办法识别客户端身份因此会导致会话异常