首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入理解跨站点 WebSocket 劫持漏洞的原理及防范(4)

深入理解跨站点 WebSocket 劫持漏洞的原理及防范(4)

防范跨站点 WebSocket 劫持攻击前文介绍了跨站点 WebSocket 劫持漏洞原理和检测,相信读者已经明白它的危害,接下来我们谈谈如何防范这个漏洞。这个漏洞的原理听起来略微复杂,但幸运的是测试起来相对比较简单,那么修复会不会也很简单。很多读者会想到,不就是在服务器代码中检查 Origin 参数嘛。是的,检查 Origin 很有必要,但不充分。笔者推荐大家要在服务器端的代码中增加 Origin 检查,如果客户端发来的 Origin 信息来自不同域,建议服务器端拒绝这个请求,发回 403 错误响应拒绝连接。
WebSocket 服务器端 Origin 检查笔者采用了 Java EE 技术编写的 WebSocket 测试应用,Java EE 的 WebSocket                                API 中提供了配置器允许开发人员重写配置用来拦截检查协议握手过程。笔者在文章附录的源代码中已经包含了这部分代码,下面简单介绍一些核心类和配置。如果对 Java                                EE WebSocket API 不太熟悉的读者,建议可以先查阅相关规范。
1. 首先编写一个 WebSocket 服务器终端的配置器,如清单 4 所示继承并重写 checkOrigin 方法。注意,笔者忽略了没有 Origin 的场景,这一点要视各个应用的实际情况而定,如果有非浏览器客户端的话,则需要加上这一个检查。同时建议非浏览器客户端参见下文的令牌机制。
清单                                        4. WebSocket 源检查配置器
1
2
3
4
5
6
7
8
9
10
11
public class CustomConfigurator extends ServerEndpointConfig.Configurator {

private static final String ORIGIN = "http://jeremy.laptop:8080";

@Override
public boolean checkOrigin(String originHeaderValue) {
if(originHeaderValue==null || originHeaderValue.trim().length()==0)
return true;
return ORIGIN.equals(originHeaderValue);
}
}




2. 然后将该配置器关联到 WebSocket 服务器代码中。
清单                                        5. 配置 WebSocket 源检查
1
2
3
4
5
6
7
8
9
10
11
@ServerEndpoint(value = "/query", configurator = CustomConfigurator.class)
public class WebSocketTestServer {
@OnMessage
public void onMessage(String message, Session session)
throws IOException, InterruptedException {
session.getBasicRemote().sendText("We got your query: " + message
+ "\nPlease wait for a while, we will response to you later.");
Thread.sleep(5000);
session.getBasicRemote().sendText("Sorry, we did not find the answer.");
}
}




3. 重新打包发布 WebSocket 应用程序。
有兴趣的读者可以自己尝试,如果补上以上代码后,重播篡改的 WebSocket 握手协议请求会收到 403 错误。
WebSocket 令牌机制以上看起来很美好,但是仅仅检查 Origin 远远不够,别忘记了,如果 WebSocket 的客户端不是浏览器,非浏览器的客户端发来的请求根本就没有 Origin。除此之外,我们要记得,恶意网页是可以伪造 Origin 头信息的。更彻底的解决方案还是要借鉴 CSRF 的解决方案-令牌机制。
鉴于篇幅原因,笔者就不详细贴出整个设计和代码,建议读者参照以下概要设计提高 WebSocket 应用的安全。
1. 服务器端为每个 WebSocket 客户端生成唯一的一次性 Token;
2.                                客户端将 Token 作为 WebSocket 连接 URL 的参数(譬如 ws://echo.websocket.org/?token=randomOneTimeToken),发送到服务器端进行 WebSocket 握手连接;
3.                                服务器端验证 Token 是否正确,一旦正确则将这个 Token 标示为废弃不再重用,同时确认 WebSocket 握手连接成功;如果 Token 验证失败或者身份认证失败,则返回 403 错误。
这个方案里的 Token 设计是关键,笔者推荐的方案是为登录用户生成一个 Secure                                Random 存储在 Session 中,然后利用对称加密(譬如 AES GCM)加密这个 Secure                                Random 值作为令牌,将加密后的令牌发送给客户端用来进行连接。这样每个 Session 有一个唯一的随机数,每个随机数可以通过对称加密生成若干份一次性令牌。用户即便通过不同终端通过 WebSocket 连接到服务器,服务器可以在保障令牌唯一且一次性使用的前提下,依然能将不同通道中的信息关联到同一用户中。
可能存在另外一个设计思路,在 WebSocket 消息中增加令牌和身份信息,但笔者觉得这样的设计有悖于 WebSocket 的设计思想,而且增加了不必要的网络负载。抛砖引玉,欢迎读者提供更好的设计方案。
总结本文笔者跟读者分享了对 WebSocket 协议握手的理解,并在此基础上阐述了跨站点 WebSocket 劫持漏洞的原理。正如文中所提,已知的各类 WebSocket 漏洞中,只有这个是广泛存在于 Web 应用代码中的漏洞。笔者同时分享了检测跨站点 WebSocket 劫持漏洞的方法,并且基于 Java                                EE 技术介绍了漏洞的修复办法,以及更全面的基于令牌机制的安全解决方案。
返回列表