Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
gregturn opened this issue Feb 19, 2020 · 1 comment

Comments

@gregturn
Copy link
Contributor

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

@krpors
Copy link

krpors commented Feb 7, 2023

Can confirm this exact same issue is still occurring using spring-boot-starter-web-services:2.6.6. So far, the workaround seems to work fine, but it did give some debugging headaches before I could pinpoint the problem.

@gregturn gregturn added this to the 4.0.3 milestone Apr 5, 2023
@gregturn gregturn self-assigned this Apr 5, 2023
gregturn pushed a commit that referenced this issue Apr 5, 2023
Ensure TTL values are propagated properly through Wss4j's security interceptor.

Resolves #1150.
Original pull request: #1185.
gregturn pushed a commit that referenced this issue Apr 5, 2023
Ensure TTL values are propagated properly through Wss4j's security interceptor.

Resolves #1150.
Original pull request: #1185.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants