SEC-2830: Cleanup disabling Same Origin SockJS
- Defaults for properties false - Add XML Namespace support
This commit is contained in:
parent
b9e2a57131
commit
b9563f6102
|
@ -88,7 +88,7 @@ public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer extends A
|
|||
public final void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
ChannelSecurityInterceptor inboundChannelSecurity = inboundChannelSecurity();
|
||||
registration.setInterceptors(securityContextChannelInterceptor());
|
||||
if(sameOriginEnforced()) {
|
||||
if(!sameOriginDisabled()) {
|
||||
registration.setInterceptors(csrfChannelInterceptor());
|
||||
}
|
||||
if(inboundRegistry.containsMapping()) {
|
||||
|
@ -100,16 +100,16 @@ public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer extends A
|
|||
/**
|
||||
* <p>
|
||||
* Determines if a CSRF token is required for connecting. This protects against remote sites from connecting to the
|
||||
* application and being able to read/write data over the connection. The default is true.
|
||||
* application and being able to read/write data over the connection. The default is false (the token is required).
|
||||
* </p>
|
||||
* <p>
|
||||
* Subclasses can override this method to disable CSRF protection
|
||||
* </p>
|
||||
*
|
||||
* @return true if a CSRF is required for connecting, else false
|
||||
* @return false if a CSRF token is required for connecting, else true
|
||||
*/
|
||||
protected boolean sameOriginEnforced() {
|
||||
return true;
|
||||
protected boolean sameOriginDisabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,7 +170,7 @@ public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer extends A
|
|||
}
|
||||
|
||||
public void afterSingletonsInstantiated() {
|
||||
if(!sameOriginEnforced()) {
|
||||
if(sameOriginDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
|
||||
private static final String ID_ATTR = "id";
|
||||
|
||||
private static final String DISABLED_ATTR = "same-origin-disabled";
|
||||
|
||||
private static final String PATTERN_ATTR = "pattern";
|
||||
|
||||
private static final String ACCESS_ATTR = "access";
|
||||
|
@ -105,6 +107,8 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
ManagedMap<BeanDefinition,String> matcherToExpression = new ManagedMap<BeanDefinition, String>();
|
||||
|
||||
String id = element.getAttribute(ID_ATTR);
|
||||
boolean sameOriginDisabled = Boolean.parseBoolean(element.getAttribute(DISABLED_ATTR));
|
||||
|
||||
|
||||
List<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_MESSAGE);
|
||||
for(Element interceptMessage : interceptMessages) {
|
||||
|
@ -137,6 +141,7 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
} else {
|
||||
BeanDefinitionBuilder mspp = BeanDefinitionBuilder.rootBeanDefinition(MessageSecurityPostProcessor.class);
|
||||
mspp.addConstructorArgValue(inSecurityInterceptorName);
|
||||
mspp.addConstructorArgValue(sameOriginDisabled);
|
||||
context.registerWithGeneratedName(mspp.getBeanDefinition());
|
||||
}
|
||||
|
||||
|
@ -180,8 +185,11 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
|
||||
private final String inboundSecurityInterceptorId;
|
||||
|
||||
public MessageSecurityPostProcessor(String inboundSecurityInterceptorId) {
|
||||
private final boolean sameOriginDisabled;
|
||||
|
||||
public MessageSecurityPostProcessor(String inboundSecurityInterceptorId, boolean sameOriginDisabled) {
|
||||
this.inboundSecurityInterceptorId = inboundSecurityInterceptorId;
|
||||
this.sameOriginDisabled = sameOriginDisabled;
|
||||
}
|
||||
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
|
@ -212,7 +220,9 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
}
|
||||
ManagedList<Object> interceptors = new ManagedList();
|
||||
interceptors.add(new RootBeanDefinition(SecurityContextChannelInterceptor.class));
|
||||
interceptors.add(new RootBeanDefinition(CsrfChannelInterceptor.class));
|
||||
if(!sameOriginDisabled) {
|
||||
interceptors.add(new RootBeanDefinition(CsrfChannelInterceptor.class));
|
||||
}
|
||||
interceptors.add(registry.getBeanDefinition(inboundSecurityInterceptorId));
|
||||
|
||||
BeanDefinition inboundChannel = registry.getBeanDefinition(CLIENT_INBOUND_CHANNEL_BEAN_ID);
|
||||
|
@ -226,6 +236,9 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
}
|
||||
|
||||
private void addCsrfTokenHandshakeInterceptor(BeanDefinition bd) {
|
||||
if(sameOriginDisabled) {
|
||||
return;
|
||||
}
|
||||
String interceptorPropertyName = "handshakeInterceptors";
|
||||
ManagedList<? super Object> interceptors = new ManagedList<Object>();
|
||||
interceptors.add(new RootBeanDefinition(CsrfTokenHandshakeInterceptor.class));
|
||||
|
|
|
@ -279,6 +279,9 @@ websocket-message-broker =
|
|||
websocket-message-broker.attrlist &=
|
||||
## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.
|
||||
attribute id {xsd:token}?
|
||||
websocket-message-broker.attrlist &=
|
||||
## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.
|
||||
attribute same-origin-disabled {xsd:boolean}?
|
||||
|
||||
intercept-message =
|
||||
## Creates an authorization rule for a websocket message.
|
||||
|
|
|
@ -870,6 +870,14 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="same-origin-disabled" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Disables the requrement for CSRF token to be present in the Stomp headers (default false).
|
||||
Changing the default is useful if it is necessary to allow other origins to make SockJS
|
||||
connections.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="intercept-message">
|
||||
<xs:annotation>
|
||||
|
|
|
@ -263,6 +263,24 @@ class WebSocketMessageBrokerConfigTests extends AbstractXmlConfigTests {
|
|||
expected.cause instanceof InvalidCsrfTokenException
|
||||
}
|
||||
|
||||
def 'messages of type CONNECT disabled valid CsrfToken'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket('same-origin-disabled':true) {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
when: 'websocket of type CONNECTION is sent without CsrfTOken'
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
|
||||
Message<?> message = message(headers,'/authentication')
|
||||
clientInboundChannel.send(message)
|
||||
|
||||
then: 'CSRF Protection blocks the Message'
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def 'websocket with no id does not override customArgumentResolvers'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
|
|
|
@ -408,8 +408,8 @@ public class AbstractSecurityWebSocketMessageBrokerConfigurerTests {
|
|||
static class CsrfDisabledSockJsSecurityConfig extends SockJsSecurityConfig {
|
||||
|
||||
@Override
|
||||
protected boolean sameOriginEnforced() {
|
||||
return false;
|
||||
protected boolean sameOriginDisabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7815,6 +7815,9 @@ If additional control is necessary, the id can be specified and a ChannelSecurit
|
|||
[[nsa-websocket-message-broker-id]]
|
||||
* **id** A bean identifier, used for referring to the ChannelSecurityInterceptor bean elsewhere in the context. If specified, Spring Security requires explicit configuration within Spring Messaging. If not specified, Spring Security will automatically integrate with the messaging infrastructure as described in <<nsa-websocket-message-broker>>
|
||||
|
||||
[[nsa-websocket-message-broker-same-origin-disabled]]
|
||||
* **same-origin-disabled** Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.
|
||||
|
||||
[[nsa-websocket-message-broker-children]]
|
||||
===== Child Elements of <websocket-message-broker>
|
||||
|
||||
|
|
Loading…
Reference in New Issue