-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
websocket 项目笔记[1] - 鉴权、安全、心跳 #15
Comments
2020 - 07 -24 补充了安全建立信道的一些方法 ① 请求头参数签名 ② 检查origin ③ 一次性token |
忍不住点了个赞 |
勘误: |
mark |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前言
最近在做一个行情模块,后端同学建议直接上
websocket
练练手,也符合业界基操。这里就记录一些开发中遇到了一些问题,聊一聊解决方案。这里不再去一点点陈列websocket
的知识点,主要会围绕项目的痛点来说。ps: 食用本文时,建议出发点需要向下沉,从
传输层
开始思考,做类似http
(应用层)需要完成的事。选型
第三方lib
项目当前的前后端选型如上表,因为
websocket
是一个基于tcp
的应用层协议,就像http
客户端服务器约定的请求头
、响应头
、cookie
等约定,和一发一收
交互形式,websocket
在使用的时候相当于将这部分约定的权利,重新交给了我们开发者。那么如果考虑使用
websockt
的lib
进行项目构建时,则需要考察该方案在两端是否都有实现的方案,原生封装
因为这次的模块是
websocket
尝鲜,所以没有考虑太多,最后决定前端
这边使用浏览器原生支持Websocket
对象,根据这次的要求进行简答的封装,先趟趟坑,正式上线后再慢慢考察框架。开工前,几个前端小伙伴做到了一起,提出了自己对这个
SDK
的期望。websocket
还是http
,请自己在接口层
封装好。lib
,req
和res
的interceptor
还是要有的吧。axios
都习惯了,接口返回的是一个请求的Promise
,这次也最后保持一致吧办公室不大,后端的同学也听到了讨论,附和到:
信道复用
实现无刷新请求。可别忘啦,后期我们还是要做服务端消息推送的。deadline
了...嘚嘞,上马开工...
鉴权
项目当前的鉴权是依赖用户登录后,服务端下发用户
token
到cookie
中,搭配header
中的某个字段进行使用。信道建立时鉴权
由于
websocket
在传输数据的时候,并不存在和http
协议一样的cookie
、request header
机制。但信道建所用的请求,仍是http
请求,你也一定见过这个请求的报文。前端视角
服务端视角
ps: 对应的框架实现,可以参考
ws - npm
模块的verifyClient
的用法,传送门👉数据传输时鉴权
数据传输时的鉴权建议基于
信道建立时鉴权
的方案,用户第一次认证后,回传给客户端一个类似token
的令牌,用户在每一次使用websocket
进行数据传输时,则需要回传这个token
到服务端进行验证。ps: 没有什么神秘的,这里其实相当于实现了个手动
cookie
。心跳机制
首先说明一点,心跳机制在
RFC
协议中没有做规定,原则上一个连接可以无限制时间去连接,但是我们知道,服务器的内存、打开文件数量是有限的,特别是需要在同一个时间服务更多用户,则需要及时发现并断开那些已经不在线
、不活跃
的连接。ps: 比如做一个内部系统、公司大屏什么的,连接数不多,可以不需要心跳机制也行。
基础的心跳
client
server
调皮的nginx
项目开发完的总结会中,查看日志才发现
webscoket
连接从未因为达到过上限10 min
未发送心跳包而断开,倒是发现不少60 s
断开的日志。后端同学恍然大悟 ----nginx
。为了可以统一在业务代码中处理心跳机制,而不是通过
nginx
来实现,所以我们将针对websocket
请求的nginx
配置调整了一下天有不测之风云
你一定知道客户端关闭
webcsocket
的close
语句但是在各个种情况下断开的
websocket
,对端又是否能够正常知晓呢?ws.close()
刷新浏览器
关闭浏览器1
关闭浏览器2
kill 命令
杀死浏览器进程突然断网
手痒拔网线
进电梯
跨域 与 安全
跨站点 WebSocket 劫持
信道建立依赖于http
若是项目验证身份的token是保存在cookie中的,并且我们知道
websocket
的信道建立是要通过http
协议的upgrade
完成的,那么也就存在浏览器中的CSRF
问题。不存在跨域
也就是说在浏览器层面,不需要跨域访问的资源的服务器返回
Access-Control-Allow-Origin
的Response Header
,数据仍然能够正常返回并且解析。原本
打算的方案针对普通的CSRF问题,先前的做法是在接口的
HTTP
请求头中,添加自定义的请求签名自定义头字段。这样做可以基本做到发起请求的页面,是出自我们自己的业务代码,而其他伪造请求的代码,会因为得不到签名字段而被后端拦截掉。不支持修改的请求头
相同的,也想在websocket
信道建立
的请求中照葫芦画瓢。但实践中发现,不同于
http
请求,websocket
请求的http Connect-upgrade
请求是浏览器内部发出的,市面上常见的浏览器都不支持我们对请求头进行编写、拓展、删除。所以这个方法行不通。
可行的方案
来了看看
rfc
是如何建议我们解决问题的吧其中蓝色部分已经支出,我们可以通过
信道建立
的http
请求的origin
字段进行校验,服务端可以直接拒绝掉非本站点发起的请求。补充的方案 - token
这次的项目虽然只是web,但我们进一步司考,若发起请求(重放请求)的攻击方环境并不是浏览器呢?是客户端,甚至是脚本拦截请求后的重放呢?
这时候,我们就需要给信道建立的http请求加上更加严格的束缚 - 一次性过期的Token。
可行的实际过程可以是:
补充
这里补充说一下
websocket
请求头中的一些字段,算是项目安全决策的一些辅助知识。request
Sec-WebSocket-Key
: 是随机的字符串,用于后续校验。Origin
: 请求源Upgrade
: websocketConnection
: Upgraderesponse
Sec-WebSocket-Accept
: 用匹配寻找客户端连接的值,计算公式为toBase64(sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ) )
这里的
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
为魔术字符串,为常量。总结
websocket
是基于tcp
上的应用层协议,http
遇到的问题websocket
都会遇到,鉴权
、保活
、签名
,这些在http
都有现成基础可以操作,在websocket
都需要使用者进行设计实现。本文了解了
websocket
与后端部分传输信道的问题,下一篇则会着重讲讲如何实现一个简单的符合业务需求的websocket
请求lib
。参考资料
[1] 深入理解跨站点 WebSocket 劫持漏洞的原理及防范
[2] How to Use Websockets in Golang: Best Tools and Step-by-Step Guide
[3] WebSocket 的鉴权授权方案 - Mo Ye
[4] RFC - The WebSocket Protocol
The text was updated successfully, but these errors were encountered: