Skip to content

make the SAMLSSOResponseValidator replay check atomic - #3625

Merged
coheigea merged 2 commits into
apache:mainfrom
rootvector2:saml-sso-replay-check-atomic
Oct 26, 2026
Merged

coheigea merged 2 commits into
apache:mainfrom
rootvector2:saml-sso-replay-check-atomic

Conversation

@rootvector2

Copy link
Copy Markdown
Contributor

SAMLSSOResponseValidator.validateSubjectConfirmation replay-checks a POST binding assertion with a non-atomic contains then putId on the TokenReplayCache, so concurrent submissions of one captured response all pass the lookup before any of them is cached and are all accepted (reproducer: 8 concurrent submissions of the same assertion id, accepted 8 times against both EHCacheTokenReplayCache and JCacheTokenReplayCache), found while reviewing the JAX-RS SAML SSO side of #3622, and fixed by adding TokenReplayCache.putIdIfAbsent backed by the atomic putIfAbsent of Ehcache and JCache (synchronized default for custom caches) and using it in the validator, with regression tests for both caches and the validator.

SAMLSSOResponseValidator.validateSubjectConfirmation guarded a POST
binding assertion against replay with contains() followed by putId() on
the TokenReplayCache, so concurrent submissions of the same captured
response all passed the lookup before any of them was cached, and were
all accepted.

Add TokenReplayCache.putIdIfAbsent, backed by the atomic putIfAbsent of
Ehcache and JCache (with a synchronized default for custom caches), and
use it in the validator. This is the JAX-RS SAML SSO counterpart of the
WS-Security JCacheReplayCache change in apache#3622.
@reta

reta commented Oct 24, 2026

Copy link
Copy Markdown
Member

@rootvector2 isn't it the same as #3622 ? (CC @coheigea )?

@rootvector2

Copy link
Copy Markdown
Contributor Author

Not the same code path. #3622 only touched the WS-Security JCacheReplayCache in rt/ws/security (the WSS4J ReplayCache.addIfAbsent override). The JAX-RS SAML SSO module has its own TokenReplayCache interface with separate EHCacheTokenReplayCache / JCacheTokenReplayCache implementations, and SAMLSSOResponseValidator.validateSubjectConfirmation on main still does replayCache.contains(id) followed by replayCache.putId(id, expires), so concurrent POSTs of the same captured response are all accepted. I found it while checking whether the SSO side had the same pattern after #3622. This PR is the equivalent fix for that module: TokenReplayCache.putIdIfAbsent backed by putIfAbsent in both caches, and the validator switched over to it.

@coheigea
coheigea merged commit d27cfb0 into apache:main Oct 26, 2026
5 checks passed
coheigea pushed a commit that referenced this pull request Oct 26, 2026
* make the SAMLSSOResponseValidator replay check atomic

SAMLSSOResponseValidator.validateSubjectConfirmation guarded a POST
binding assertion against replay with contains() followed by putId() on
the TokenReplayCache, so concurrent submissions of the same captured
response all passed the lookup before any of them was cached, and were
all accepted.

Add TokenReplayCache.putIdIfAbsent, backed by the atomic putIfAbsent of
Ehcache and JCache (with a synchronized default for custom caches), and
use it in the validator. This is the JAX-RS SAML SSO counterpart of the
WS-Security JCacheReplayCache change in #3622.

* reject null and empty ids in the default putIdIfAbsent, add timeouts to the cache concurrency test

(cherry picked from commit d27cfb0)
coheigea pushed a commit that referenced this pull request Oct 26, 2026
* make the SAMLSSOResponseValidator replay check atomic

SAMLSSOResponseValidator.validateSubjectConfirmation guarded a POST
binding assertion against replay with contains() followed by putId() on
the TokenReplayCache, so concurrent submissions of the same captured
response all passed the lookup before any of them was cached, and were
all accepted.

Add TokenReplayCache.putIdIfAbsent, backed by the atomic putIfAbsent of
Ehcache and JCache (with a synchronized default for custom caches), and
use it in the validator. This is the JAX-RS SAML SSO counterpart of the
WS-Security JCacheReplayCache change in #3622.

* reject null and empty ids in the default putIdIfAbsent, add timeouts to the cache concurrency test

(cherry picked from commit d27cfb0)
@rootvector2

Copy link
Copy Markdown
Contributor Author

Thanks for the review and merge, glad the SSO side is covered too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants