|
For the latest stable version, please use Spring Integration 7.1.0! |
Security in Spring Integration
Security is one of the important functions in any modern enterprise (or cloud) application.
Moreover, it is critical for distributed systems, such as those built on Enterprise Integration Patterns.
Messaging independence and loose coupling let target systems communicate with each other with any type of data in the message’s payload.
We can either trust all those messages or secure our service against “infecting” messages.
Starting with version 6.3 the whole spring-integration-security module is removed in favor of an API proposed by the more common spring-security-messaging library.
|
Securing channels
To secure message channels in the integration flow, an AuthorizationChannelInterceptor has to be added to those channels, or it can be configured as a global channel interceptor with a respective pattern:
-
Java
-
XML
@Bean
@GlobalChannelInterceptor(patterns = "secured*")
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
}
<channel-interceptor pattern="securedChannel*">
<beans:bean class="org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="hasAnyRole">
<beans:constructor-arg>
<beans:array>
<beans:value>ADMIN</beans:value>
<beans:value>PRESIDENT</beans:value>
</beans:array>
</beans:constructor-arg>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</channel-interceptor>
See Global Channel Interceptor Configuration for more information.
Security Context Propagation
To be sure that our interaction with the application is secure, according to its security system rules, we should supply some security context with an authentication (principal) object.
The Spring Security project provides a flexible, canonical mechanism to authenticate our application clients over HTTP, WebSocket, or SOAP protocols (as can be done for any other integration protocol with a simple Spring Security extension).
It also provides a SecurityContext for further authorization checks on the application objects, such as message channels.
By default, the SecurityContext is tied to the execution state of the current Thread by using the (ThreadLocalSecurityContextHolderStrategy).
It is accessed by an AOP (Aspect-oriented Programming) interceptor on secured methods to check (for example) whether that principal of the invocation has sufficient permissions to call that method.
This works well with the current thread.
Often, though, processing logic can be performed on another thread, on several threads, or even on external systems.
Standard thread-bound behavior is easy to configure if our application is built on the Spring Integration components and its message channels.
In this case, the secured objects can be any service activator or transformer, secured with a
MethodSecurityInterceptor in their <request-handler-advice-chain> (see Adding Behavior to Endpoints) or even MessageChannel (see Securing channels, earlier).
When using DirectChannel communication, the SecurityContext is automatically available, because the downstream flow runs on the current thread.
However, in the cases of the QueueChannel, ExecutorChannel, and PublishSubscribeChannel with an Executor, messages are transferred from one thread to another (or several) by the nature of those channels.
In order to support such scenarios, we have two choices:
-
Transfer an
Authenticationobject within the message headers and extract and authenticate it on the other side before secured object access. -
Propagate the
SecurityContextto the thread that receives the transferred message.
This is implemented as a org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor in the spring-security-messaging module, which can be added to any MessageChannel or configured as a @GlobalChannelInterceptor.
The logic of this interceptor is based on the SecurityContext extraction from the current thread (from the preSend() method) and its populating to another thread from the postReceive() (beforeHandle()) method.
See the SecurityContextPropagationChannelInterceptor Javadocs for more information.
Propagation and population of SecurityContext is just one half of the work.
Since the message is not an owner of the threads in the message flow, and the system should be sure that it is secured against any incoming messages, the SecurityContext has to be cleaned up from ThreadLocal.
The SecurityContextPropagationChannelInterceptor provides the afterMessageHandled() interceptor method implementation.
It cleans up operation by freeing the thread at the end of invocation from that propagated principal.
This means that, when the thread that processes the handed-off message finishes processing the message (successful or otherwise), the context is cleared so that it cannot inadvertently be used when processing another message.
|
When working with an asynchronous gateway, you should use an appropriate
|
Trusting Data
If the data integrity, transferred in the system via messages, is not guaranteed, the authentication and authorization control might not be enough for the application to perform correctly, since even a valid user may produce malicious data.
The validation and filtration must be applied to the message payload and its headers if the source of that message is not trusted.
This applies not only to the type information carried in headers with potential deserialization gadgets but also to those use-cases where the target destination for the message is determined from a header of that message.
These scenarios fit into the Return Address pattern.
Some vulnerabilities have been identified related to email reply addresses, specifically involving injection, spoofing, and unauthorized redirection of replies.
Therefore, such metadata in those standard message headers (MqttHeaders.TOPIC, RedisHeaders.KEY, MailHeaders.TO, MailHeaders.CC, MailHeaders.BCC, AvroHeaders.TYPE etc.), if they are carried (or re-mapped) from an external untrusted source, should be validated or stripped before they are used in the target endpoint.
One of the options to avoid metadata spoofing is to not allow it to enter the application at all.
The inbound channel adapters (and gateways) in Spring Integration provide a HeaderMapper strategy to filter what headers should be mapped from the source system.
Another component is the HeaderFilter to remove unwanted headers in the flow.
However, sometimes the logic of the application relies on those headers even if they come from an external system.
For this purpose values of those headers should be validated, for example, using MessageFilter endpoint, or MessageSelectingInterceptor.
Starting with version 6.5.9, Spring Integration provides an AllowListMessageHeaderSelector to apply simple patterns on the message header values:
@Bean
public IntegrationFlow mqttOutFlow() {
return f -> f
.intercept(new MessageSelectingInterceptor(
new AllowListMessageHeaderSelector(MqttHeaders.TOPIC, "test*", "!test*malicious*")))
.handle(new MqttPahoMessageHandler("mqttOut", pahoClientFactory()));
}
In the example above only values starting with test would be accepted for MqttHeaders.TOPIC.
And those with malicious sub-string (even if they start with test) won’t be accepted.
See AllowListMessageHeaderSelector Javadocs for more information.
Sensitive Metadata
The following is the list of standard Spring Integration headers that are involved in some message processing decisions internally in the framework. The mentioned above validation and filtering might be applied to them before the message goes to the respective processing if the source of the message is not trusted.
-
spring-integration-core-
replyChannel -
errorChannel -
routingSlip -
gatherResultChannel -
originalErrorChannel -
avro_type -
proto_type
-
-
spring-integration-file-
file_name
-
-
spring-integration-ip-
ip_ackTo
-
-
spring-integration-jmx-
jmx_objectName -
jmx_operationName
-
-
spring-integration-kafka-
kafka_topic
-
-
spring-integration-mail-
mail_to -
mail_cc -
mail_bcc
-
-
spring-integration-mqtt-
mqtt_topic
-
-
spring-integration-redis-
redis_key -
redis_mapKey
-
-
spring-integration-xmpp-
xmpp_to
-
-
spring-integration-zip-
zip_entryFilename
-