-
Notifications
You must be signed in to change notification settings - Fork 317
Description
mightybeaker opened SWS-1084 and commented
Background
I have spring web services deployed to remote hosts offshore that may not be using NTP services to keep their clocks synchronised resulting in potential skewing of those server system clocks. This is out of my control and causes an issue with the web service ws-security handling specifically relating to the ws-security header timestamp validation of the received message.
I attempted to increase the time to live attributes on the definition of the Wss4jSecurityInterceptor bean for example by setting these 3:
<property name="timestampStrict" value="${server.wss.timestampStrict}" />
<property name="validationTimeToLive" value="${server.wss.validation.ttl}" />
<property name="futureTimeToLive" value="${server.wss.future.ttl}" />
The full bean definition:
<bean id= "wss4jInterceptor" class="org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor">
<!-- Securing the outgoing message -->
<property name="securementActions" value="${server.wss.actions}" />
<property name="timestampPrecisionInMilliseconds" value="true" />
<property name="securementTimeToLive" value="${server.wss.securement.ttl}" />
<property name="securementSignatureKeyIdentifier" value="DirectReference" />
<property name="securementUsername" value="${ws.server.ks.sig.reply.privatekey.alias}" />
<property name="securementPassword" value="${ws.server.ks.sig.reply.privatekey.password}" />
<property name="securementSignatureParts" value="{}
{http://schemas.xmlsoap.org/soap/envelope/}
Body" />
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean">
<property name="keyStorePassword" value="${ws.server.ks.sig.reply.password}" />
<property name="keyStoreLocation" value="${ws.server.ks.sig.reply.url}" />
</bean>
</property>
<!-- Validating the incoming message -->
<property name="validationActions" value="${server.wss.actions}" />
<property name="timestampStrict" value="${server.wss.timestampStrict}" />
<property name="validationTimeToLive" value="${server.wss.validation.ttl}" />
<property name="futureTimeToLive" value="${server.wss.future.ttl}" />
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean">
<property name="keyStorePassword" value="${server.ks.sig.rqst.password}" />
<property name="keyStoreLocation" value="${server.ks.sig.rqst.url}" />
</bean>
</property>
<property name="exceptionResolver" ref="myExceptionResolver"/>
</bean>
Problem
Strangely, the problem did not improve. The timestamp validation was still failing. I had increased the ttl values to 420 seconds (7 minutes) but it appeared the update had no effect.
I've done some analysis debugging the Wss4jSecurityInterceptor
locally and came up with the following observations:
Default values from Wss4jSecurityInterceptor
:
private boolean timestampStrict = true;
private int validationTimeToLive = 300;
private int futureTimeToLive = 60;
The TimestampValidator.validate()
method is actually called twice within the Wss4jSecurityInterceptor.validateMessage()
processing.
Firstly in the securityEngine.processSecurityHeader()
and subsequently in the verifyTimestamp()
method.
It appears that the TimestampValidator.validate()
method requires an initialised RequestData
object passed in with the various time to live attributes propagated from the attributes of the Wss4jSecurityInterceptor
.
Within the code of the Wss4jSecurityInterceptor.validateMessage()
method there is an attempt to do just that via initializeValidationRequestData()
:
@Override
protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
throws WsSecurityValidationException {
...
RequestData validationData = initializeValidationRequestData(messageContext);
WSHandlerResult result = securityEngine
.processSecurityHeader(elem, validationData);
...
verifyTimestamp(result);
However, the initializeValidationRequestData()
method doesn't propagate the attributes. Therefore the processSecurityHeader()
processing ends up using the default values contained in the RequestData
object being:
/**
* The time in seconds between creation and expiry for a Timestamp. The default
* is 300 seconds (5 minutes).
*/
private int timeStampTTL = 300;
/**
* The time in seconds in the future within which the Created time of an incoming
* Timestamp is valid. The default is 60 seconds.
*/
private int timeStampFutureTTL = 60;
This is why the overall ws-security header validation still fails because it's using these default values instead of those I had explicitly set on the Wss4jSecurityInterceptor
bean.
Workaround
To address this, I extended Wss4jSecurityInterceptor
and overrode the initializeValidationRequestData()
method as follows:
/**
* Fix: The parent calls a processSecurityHeader method on the <code>WSSecurityEngine</code>
* prior to calling the verifyTimestamp as part of the validateMessage method.
* The problem is that the necessary time to live attributes are not propagated
* during the initializeValidationRequestData method in the parent so we need
* to do it explicitly here unfortunately.
*/
@Override
protected RequestData initializeValidationRequestData(MessageContext messageContext)
{
RequestData requestData = new RequestData();
requestData = super.initializeValidationRequestData(messageContext);
requestData.setTimeStampFutureTTL(futureTimeToLive);
requestData.setTimeStampTTL(validationTimeToLive);
requestData.setTimeStampStrict(timestampStrict);
return requestData;
}
This ensures that the RequestData
object is correctly configured before either of the two calls to verify the timestamps. Without this, the processSecurityHeader()
would fail the timestamp validation as it wasn't picking up the specified ttl values from the bean.
Potential Fix
I think the initializeValidationRequestData()
method in Wss4jSecurityInterceptor
class could propagate the values to the RequestData
object as per my overridden method above and that would solve this bug.
Perhaps then the correctly initialised RequestData
object could be passed to the verifyTimestamp()
method too precluding the need for a new object to be created again inside that method.
Affects: 3.0.8