23. WebSocket Security(网络套接字安全)
spring Security 4增加了对保护spring的网络套接字支持的支持。本节描述如何使用Spring Security的网络套接字支持。
您可以在samples/javaconfig/chat中找到一个完整的WebSocket安全性工作示例。
直接JSR-356支持
Spring Security不提供直接的JSR-356支持,因为这样做没有什么价值。这是因为格式未知,所以Spring无法保护未知格式。此外,JSR-356没有提供拦截消息的方法,因此安全性将是相当有侵害性的。
23.1 WebSocket Configuration(网络套接字配置)
Spring Security4.0通过Spring 消息传递抽象引入了对网络套接字的授权支持。要使用Java配置配置授权,只需扩展抽象安全性套接字消息拦截配置器并配置消息安全性数据源注册表。例如:
这将确保:
1、 任何入站连接消息都需要有效的CSRF令牌来实施相同的源策略。
2、对于任何入站请求,SecurityContextHolder都是在简单用户头属性中填充用户的。
3、我们的信息需要适当的授权。具体来说,任何以“/user/”开头的入站消息都需要ROLE_USER。关于授权的更多细节可以在第23.3节“网络套接字授权”中找到。
Spring Security还为保护网络套接字提供了XML命名空间支持。类似的基于XML的配置如下所示:
这将确保:
1、 任何入站连接消息都需要有效的CSRF令牌来实施相同的源策略。
2、对于任何入站请求,SecurityContextHolder都是在简单用户头属性中填充用户的。
3、我们的信息需要适当的授权。具体来说,任何以“/user/”开头的入站消息都需要ROLE_USER。关于授权的更多细节可以在第23.3节“网络套接字授权”中找到。
23.2 WebSocket Authentication(网络套接字认证)
网络套接字重用与建立网络套接字连接时在超文本传输协议请求中发现的相同的身份验证信息。这意味着HttpServletRequest上的主体将被移交给网络套接字。如果您使用的是Spring Security,则HttpServletRequest上的主体将被自动覆盖。
更具体地说,为了确保用户已经对您的网络套接字应用程序进行了身份验证,所有需要做的就是确保您设置了Spring Security来对您的基于HTTP的网络应用程序进行身份验证。
23.3 WebSocket Authorization(网络套接字授权)
Spring Security 4.0通过Spring消息传递抽象引入了对网络套接字的授权支持。要使用Java配置来配置授权,只需扩展抽象安全性套接字消息拦截配置器(AbstractSecurityWebSocketMessageBrokerConfigurer )并配置消息安全性数据源注册表(MessageSecurityMetadataSourceRegistry)。例如:
这将确保:
1、任何没有目的地的消息(即除消息类型或订阅之外的任何消息)都需要用户进行身份验证。
2、任何人都可以订阅/用户/队列/错误。
3、任何以“/app/”开头的消息都要求用户具有角色“ROLE_USER”。
4、任何以“/user/”或“/topic/friends/”开头的订阅类型的消息都需要ROLE_USER。
5、message或SUBSCRIBE类型的任何其他消息都会被拒绝。由于6,我们不需要这一步,但它说明了如何匹配特定的消息类型。
6、任何其他消息都会被拒绝。这是一个好主意,以确保您不会错过任何消息。
Spring Security还为保护网络套接字提供了XML命名空间支持。类似的基于XML的配置如下所示:
这将确保:
1、任何没有目的地的消息(即除消息类型或订阅之外的任何消息)都需要用户进行身份验证。
2、任何人都可以订阅/用户/队列/错误。
3、任何以“/app/”开头的消息都要求用户具有角色“ROLE_USER”。
4、任何以“/user/”或“/topic/friends/”开头的订阅类型的消息都需要ROLE_USER。
5、message或SUBSCRIBE类型的任何其他消息都会被拒绝。由于6,我们不需要这一步,但它说明了如何匹配特定的消息类型。
6、任何其他消息都会被拒绝。这是一个好主意,以确保您不会错过任何消息。
23.3.1 WebSocket Authorization Notes(网络套接字授权说明)
为了正确保护您的应用程序,理解Spring的WebSocket支持非常重要。
WebSocket Authorization on Message Types(消息类型的网络套接字授权)
理解SUBSCRIBE和MESSAGE消息类型之间的区别以及它在Spring中的工作方式非常重要。
考虑一个聊天应用程序。
1、系统可以通过“/主题/系统/通知”的目的地向所有用户发送通知消息
2、客户可以通过订阅“/主题/系统/通知”来接收通知。
虽然我们希望客户端能够订阅“/topic/system/notifications”,但我们不希望让它们能够向该目的地发送消息。如果我们允许向“/topic/system/notifications”发送消息,那么客户端可以直接向该端点发送消息并模拟系统。
通常,应用程序会拒绝向以代理前缀(即“/topic/”或“/queue/”)开头的消息发送任何消息。
WebSocket Authorization on Destinations(目标上的网络套接字授权)
了解目的地是如何转变的也很重要。
考虑一个聊天应用程序。
1、用户可以通过向“/app/chat”的目的地发送消息来向特定用户发送消息。
2、应用程序看到消息,确保“发件人”属性被指定为当前用户(我们不能信任客户端)。
3、然后,应用程序使用将消息发送给接收者SimpMessageSendingOperations.convertAndSendToUser("toUser", "/queue/messages", message).
4、消息被转到“"/queue/user/messages-<sessionid>”的目的地。
使用上面的应用程序,我们希望允许我们的客户端监听“/user/queue”,它被转换为“/queue/user/messages-< session id >”。但是,我们不希望客户端能够监听“/queue/*”,因为这将允许客户端看到每个用户的消息。
一般来说,应用程序通常会拒绝向以代理前缀(即“/topic/”或“/queue/”)开头的消息发送任何SUBSCRIBE。当然,我们可能会提供一些例外来解释类似的事情
23.3.2 Outbound Messages(出站消息)
Spring包含一个名为消息流的部分,描述了消息如何在系统中流动。需要注意的是,Spring Security只能保护客户端的安全。Spring Security不会尝试保护客户端边界通道。
最重要的原因是性能。对于每一条进入的消息,通常会有更多的消息出去。我们鼓励保护对端点的订阅,而不是保护出站消息。
23.4 Enforcing Same Origin Policy(执行相同来源政策)
需要强调的是,浏览器不会对网络套接字连接强制执行相同的源策略。这是一个极其重要的考虑因素。
23.4.1 Why Same Origin?(为什么来源相同?)
考虑以下场景。一名用户访问bank.com,并对其帐户进行身份验证。同一个用户在浏览器中打开另一个标签,访问evil.com。相同来源政策确保evil.com不能向bank.com读写数据。
对于网络套接字,同一来源策略不适用。事实上,除非bank.com明确禁止,否则evil.com可以代表用户读写数据。这意味着用户可以通过网络套接字做的任何事情(即转账),evil.com可以代表该用户做。
由于SockJS试图模仿网络套接字,它也绕过了相同来源策略。这意味着开发人员在使用SockJS时需要明确地保护他们的应用程序不受外部域的影响。
23.4.2 Spring WebSocket Allowed Origin(Spring网络套接字允许的来源)
幸运的是,由于Spring 4.1.5,Spring的网络套接字和SockJS支持限制了对当前域的访问。spring安全增加了一层额外的保护,提供纵深防御。
23.4.3 Adding CSRF to Stomp Headers
默认情况下,任何连接消息类型都需要CSRF令牌。这确保了只有能够访问CSRF令牌的站点才能连接。因为只有同一个源可以访问CSRF令牌,所以不允许外部域建立连接。
通常,我们需要在一个超文本传输协议头或一个超文本传输协议参数中包含CSRF令牌。然而,SockJS 不允许这些选择。相反,我们必须在踏脚标题中包含令牌.
应用程序可以通过访问name_csrf的请求属性来获取CSRF令牌。例如,下面将允许访问JSP中的CsrfToken:
如果您使用的是静态的超文本标记语言,那么您可以在一个REST 端点上公开这个CsrfToken。例如,下面将在URL /csrf上公开CsrfToken
JavaScript可以对端点进行REST调用,并使用响应来填充头名和令牌。
我们现在可以将令牌包含在踏脚客户端中。例如:
23.4.4 Disable CSRF within WebSockets(在网络套接字中禁用CSRF)
如果您想允许其他域访问您的站点,您可以禁用Spring Security的保护。例如,在Java配置中,您可以使用以下内容:
23.5 Working with SockJS
SockJS提供后备传输来支持旧的浏览器。当使用回退选项时,我们需要放松一些安全约束,以允许SockJS使用Spring Security。
23.5.1 SockJS & frame-options
SockJS可以使用利用iframe的传输。默认情况下,Spring Security会拒绝网站被陷害,以防止点击劫持攻击。为了使基于框架的传输能够工作,我们需要配置Spring Security来允许相同的源来框架内容。
您可以使用框架选项元素来自定义框架选项。例如,下面将指示Spring Security使用“X-Frame-Options: SAMEORIGIN”,它允许在同一个域中使用iframe:
同样,您可以使用以下内容自定义框架选项,以便在Java配置中使用相同的原点:
23.5.2 SockJS & Relaxing CSRF
SockJS对任何基于超文本传输协议的传输的连接消息使用开机自检。通常,我们需要在一个超文本传输协议头或一个超文本传输协议参数中包含CSRF令牌。然而,社会团体不允许这些选择。相反,我们必须按照第23.4.3节“将CSRF添加到踏脚标头”所述,将令牌包含在踏脚标头中。
这也意味着我们需要放松对网络层的CSRF保护。具体来说,我们想为我们的连接网址禁用CSRF保护。我们不想为每个网址禁用CSRF保护。否则我们的网站将容易受到CSRF的攻击。
我们可以通过提供一个CSRF请求匹配器轻松实现这一点。我们的Java配置使这变得非常简单。例如,如果我们的踏脚端点是“/chat”,我们可以使用以下配置仅对以“/chat/”开头的网址禁用CSRF保护:
如果我们使用基于XML的配置,我们可以使用csrf@request-matcher-ref。例如: