Skip to content

Wss4jSecurityInterceptor not propagating time to live values when validating ws-security soap header timestamp [SWS-1084] #1150

@gregturn

Description

@gregturn

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions