Skip to content

Fix unbounded server-controlled retry sleep on HTTP 429 (OKTA-1235975) - #1707

Open
prachi-okta wants to merge 2 commits into
masterfrom
OKTA-1235975-unbounded-429-retry-sleep
Open

Fix unbounded server-controlled retry sleep on HTTP 429 (OKTA-1235975)#1707
prachi-okta wants to merge 2 commits into
masterfrom
OKTA-1235975-unbounded-429-retry-sleep

Conversation

@prachi-okta

Copy link
Copy Markdown
Contributor

Summary

OKTA-1235975: the SDK's 429 retry delay was computed entirely from the server-supplied x-rate-limit-reset header with no upper bound, so a malicious or MitM server could force the client thread to sleep indefinitely. Since this sleep can happen inside OAuth2ClientCredentials.applyToParams (synchronized), one blocked call could stall every other caller waiting on token refresh — a client-side DoS triggerable by untrusted response data.

  • RetryUtil.get429DelayMillis now clamps the computed delay to a maxDelayMillis ceiling.
  • OktaHttpRequestRetryStrategy derives that ceiling from retryMaxElapsed if explicitly configured, otherwise falls back to the same 20s cap already used for 503/504/IOException retries — so 429 is no longer the exception.
  • DefaultClientBuilder now actually wires clientConfig.getRetryMaxElapsed() into the strategy (previously set on ClientConfiguration but never read anywhere — dead config).
  • README updated to document the new default cap and the existing requestTimeout/setRetryMaxElapsed escape hatch for anyone who needs a longer wait.

Separately (own commit, unrelated root cause): bumps httpclient5 5.6.2 → 5.6.4 to resolve CVE-2026-64607 (connection-leak DoS in the classic I/O model on malformed Content-Encoding headers), found while chasing an unrelated stalled release:prepare run. This supersedes the existing dependabot/maven/.../httpclient5-5.6.3 branch.

Test plan

  • RetryUtilTest: normal/legitimate reset value unaffected; simulated malicious 429 (reset ~1 year out) clamps to the ceiling; Long.MAX_VALUE header-overflow attempt still bounded; cap wins over the MIN_RETRY_DELAY_MS floor; expired reset timestamp falls back to the minimum.
  • OktaHttpRequestRetryStrategyTest (new): end-to-end getRetryInterval() against a malicious 429 response — default config caps at 20s, explicit retryMaxElapsed=5 caps at 5s, non-429 exponential backoff unaffected.
  • DefaultClientBuilderTest.groovy (new tests): reflects into the real HttpClientBuilder.retryStrategy built by createHttpClientBuilder to prove the production wiring (not just the isolated strategy) bounds the malicious delay.
  • Full suite: 174/174 impl tests + 56/56 api tests passing.
  • mvn dependency:tree confirms httpclient5:5.6.4 is what's actually resolved into the build.

AI Attribution: ai-agent-authored

The 429 retry delay was computed entirely from the server-supplied
x-rate-limit-reset header with no upper bound, so a malicious or MitM
server could force the client thread to sleep indefinitely. Since this
can happen inside OAuth2ClientCredentials.applyToParams (synchronized),
one blocked thread could stall every other caller waiting on token
refresh.

RetryUtil.get429DelayMillis now clamps the computed delay to a
maxDelayMillis ceiling. OktaHttpRequestRetryStrategy derives that
ceiling from retryMaxElapsed if explicitly configured, otherwise falls
back to the same 20s cap already used for 503/504/IOException retries.
DefaultClientBuilder now actually wires clientConfig.getRetryMaxElapsed()
into the strategy, which was previously set on ClientConfiguration but
never read anywhere.

Co-Authored-By: Claude Code
httpclient5 5.6.2 fails to release the underlying connection back to
the connection manager on an invalid/unsupported Content-Encoding
header value in the classic I/O model, allowing connection pool
exhaustion (DoS). Fixed upstream in 5.6.3+; bumping to the latest
5.6.x patch release.

Co-Authored-By: Claude Code
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.

1 participant